[BUGFIX] Fix loading order for extensions 21/31021/2
authorMarkus Klein <klein.t3@mfc-linz.at>
Tue, 27 May 2014 15:36:45 +0000 (17:36 +0200)
committerMarkus Klein <klein.t3@mfc-linz.at>
Sun, 22 Jun 2014 01:18:05 +0000 (03:18 +0200)
Stick to the documented loading order of extensions.
That means following this order: sysext, global, local, composer

The same extension found in the latter will be loaded instead of
the former. Also adjust the Dependency resolver accordingly for the
edge-case of overwriting sysexts.

Resolves: #59147
Releases: 6.3, 6.2
Change-Id: I50c7849e32b309a28f792340f3ff892e516dadb8
Reviewed-on: https://review.typo3.org/31021
Reviewed-by: Markus Klein
Tested-by: Markus Klein
typo3/sysext/core/Classes/Package/DependencyResolver.php
typo3/sysext/core/Classes/Package/PackageManager.php
typo3/sysext/core/Tests/Unit/Package/DependencyResolverTest.php

index 77e496e..b23a779 100644 (file)
@@ -281,15 +281,9 @@ class DependencyResolver {
        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) {
+                       if ($basePath === '' || in_array($basePath, $package['packagePathStack'], TRUE)) {
+                               $containedExcludedPaths = array_intersect($package['packagePathStack'], $excludedPaths);
+                               if (empty($containedExcludedPaths)) {
                                        $packageKeys[] = $packageKey;
                                }
                        }
index fb713d1..1555f3c 100644 (file)
@@ -83,9 +83,9 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
         */
        public function __construct() {
                $this->packagesBasePaths = array(
-                       'local'     => PATH_typo3conf . 'ext',
-                       'global'    => PATH_typo3 . 'ext',
                        'sysext'    => PATH_typo3 . 'sysext',
+                       'global'    => PATH_typo3 . 'ext',
+                       'local'     => PATH_typo3conf . 'ext',
                        'composer'  => PATH_site . 'Packages',
                );
                $this->packageStatesPathAndFilename = PATH_typo3conf . 'PackageStates.php';
@@ -384,6 +384,10 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
                        }
 
                        $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $packagePath);
+                       // An extension might be overwritten by another one. (eg sysext overwritten with ext)
+                       // We have to remember which paths we found the extension in as
+                       // we need to know this in the DependencyResolver later.
+                       $this->packageStatesConfiguration['packages'][$packageKey]['packagePathStack'][] = $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'];
 
                        // Change this to read the target from Composer or any other source
                        $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
@@ -703,6 +707,9 @@ class PackageManager extends \TYPO3\Flow\Package\PackageManager implements \TYPO
                $newPackages = array();
                foreach (array_keys($this->packageStatesConfiguration['packages']) as $packageKey) {
                        $newPackages[$packageKey] = $this->packages[$packageKey];
+                       // Remove the packagePathStack here again. We don't need it anymore and
+                       // we don't want it in the package states file
+                       unset($this->packageStatesConfiguration['packages'][$packageKey]['packagePathStack']);
                }
                $this->packages = $newPackages;
        }
index c9daf61..274fadd 100644 (file)
@@ -94,22 +94,15 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        'A' => array(
                                'state' => 'active',
                                'dependencies' => array('B'),
+                               'packagePathStack' => array('foo')
                        ),
                        'B' => array(
                                'state' => 'active',
-                               'dependencies' => array('A')
+                               'dependencies' => array('A'),
+                               'packagePathStack' => array('foo')
                        ),
                );
-
-               $packageKeys = array_keys($unsortedPackageStatesConfiguration);
-
-               $basePathAssignment = array(
-                       array($unsortedPackageStatesConfiguration, '', array(DependencyResolver::SYSEXT_FOLDER), $packageKeys),
-                       array($unsortedPackageStatesConfiguration, DependencyResolver::SYSEXT_FOLDER, array(), array()),
-               );
-
-               $dependencyResolver = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\DependencyResolver', array('getActivePackageKeysOfType'));
-               $dependencyResolver->expects($this->any())->method('getActivePackageKeysOfType')->will($this->returnValueMap($basePathAssignment));
+               $dependencyResolver = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\DependencyResolver', array('dummy'));
                $dependencyResolver->_call('sortPackageStatesConfigurationByDependency', $unsortedPackageStatesConfiguration);
        }
 
@@ -885,4 +878,94 @@ class DependencyResolverTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $this->assertSame($expected, $path);
        }
 
+       /**
+        * Data provider for getPackageKeysInBasePathReturnsCorrectKeys
+        * @return array
+        */
+       public function getPackageKeysInBasePathReturnsCorrectKeysDataProvider() {
+               return array(
+                       'Single paths only, include all' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                               ),
+                               '',
+                               array(),
+                               array('key1', 'key2', 'key3')
+                       ),
+                       'Single paths only, include sysext' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                               ),
+                               DependencyResolver::SYSEXT_FOLDER,
+                               array(),
+                               array('key1', 'key3')
+                       ),
+                       'Single paths only, exclude sysext' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                               ),
+                               '',
+                               array(DependencyResolver::SYSEXT_FOLDER),
+                               array('key2')
+                       ),
+                       'Multiple paths, include sysext' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER, 'somewhereelse')),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                               ),
+                               DependencyResolver::SYSEXT_FOLDER,
+                               array(),
+                               array('key1', 'key3')
+                       ),
+                       'Multiple paths, exclude sysext' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER, 'somewhereelse')),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                                       'key4' => array('packagePathStack' => array('elsewhere')),
+                               ),
+                               '',
+                               array(DependencyResolver::SYSEXT_FOLDER),
+                               array('key2', 'key4')
+                       ),
+                       'Multiple paths, include somewhereelse' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER, 'somewhereelse')),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                               ),
+                               'somewhereelse',
+                               array(),
+                               array('key1', 'key2')
+                       ),
+                       'Multiple paths, include somewhereelse, exclude sysext' => array(
+                               array(
+                                       'key1' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER, 'somewhereelse')),
+                                       'key2' => array('packagePathStack' => array('somewhereelse')),
+                                       'key3' => array('packagePathStack' => array(DependencyResolver::SYSEXT_FOLDER)),
+                                       'key4' => array('packagePathStack' => array('elsewhere')),
+                               ),
+                               'somewhereelse',
+                               array(DependencyResolver::SYSEXT_FOLDER),
+                               array('key2')
+                       ),
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider getPackageKeysInBasePathReturnsCorrectKeysDataProvider
+        */
+       public function getPackageKeysInBasePathReturnsCorrectKeys(array $packageStates, $include, array $exclude, array $expectedKeys) {
+               $dependencyResolver = $this->getAccessibleMock('\TYPO3\CMS\Core\Package\DependencyResolver', array('dummy'));
+               $keys = $dependencyResolver->_call('getPackageKeysInBasePath', $packageStates, $include, $exclude);
+               $this->assertSame($expectedKeys, $keys);
+       }
 }