diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php index e4b5fa1..b27d567 100644 --- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php +++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php @@ -99,7 +99,9 @@ class ExtensionDiscovery { protected $sitePath; /** - * The profile handler object. + * The profile handler. + * + * It is used to determine the directories we want to scan modules for. * * @var \Drupal\Core\Extension\ProfileHandlerInterface */ @@ -117,7 +119,7 @@ class ExtensionDiscovery { * @param string $site_path * The path to the site. * @param Drupal\Core\Extension\ProfileHandlerInterface - * The Profile Handler instance to use. + * The Profile handler. */ public function __construct($root, $use_file_cache = TRUE, $profile_directories = NULL, $site_path = NULL, $profile_handler = NULL) { $this->root = $root; @@ -270,7 +272,7 @@ public function setProfileDirectoriesFromSettings() { $profile_directories = array_map(function($extension) { return $extension->getPath(); }, $profiles); - $this->profileDirectories = array_merge($profile_directories, $this->profileDirectories); + $this->profileDirectories = array_unique(array_merge($profile_directories, $this->profileDirectories)); } else { $this->profileDirectories[] = drupal_get_path('profile', $profile); diff --git a/core/lib/Drupal/Core/Extension/ProfileHandler.php b/core/lib/Drupal/Core/Extension/ProfileHandler.php index e3eafc4..25f61ed 100644 --- a/core/lib/Drupal/Core/Extension/ProfileHandler.php +++ b/core/lib/Drupal/Core/Extension/ProfileHandler.php @@ -10,23 +10,25 @@ class ProfileHandler implements ProfileHandlerInterface { /** * Cache for getProfiles. * - * @var array + * This cache stores the profile extensions keyed by base profile. + * + * @var \Drupal\Core\Extension\Extension[][] */ - protected $cache = array(); + protected $cache = []; /** * Cache for processing info files. * * @var array */ - protected $info_cache = array(); + protected $infoCache = []; /** * Whether we have primed the filename cache. * * @var bool */ - protected $scan_cache = FALSE; + protected $scanCache = FALSE; /** * The app root. @@ -54,7 +56,7 @@ class ProfileHandler implements ProfileHandlerInterface { * * @var int */ - private $weight; + protected $weight; /** * Constructs a new ProfileHandler. @@ -88,17 +90,20 @@ protected function getExtensionDiscovery() { /** * Return the full path to a profile. * - * Wrapper around drupal_get_path. If profile path is not available yet - * we call scan('profile') and prime the cache. + * Wrapper around drupal_get_path. If profile path is not available yet we + * call scan('profile') and prime the cache. * * @param string $profile - * Name of the profile. + * The name of the profile. + * + * @return string + * The full path to the profile. */ protected function getProfilePath($profile) { // Check to see if system_rebuild_module_data cache is primed. // @todo Remove as part of https://www.drupal.org/node/2186491. $modules_cache = &drupal_static('system_rebuild_module_data'); - if (!$this->scan_cache && !isset($modules_cache)) { + if (!$this->scanCache && !isset($modules_cache)) { $listing = $this->getExtensionDiscovery(); // Find installation profiles. This needs to happen before performing a // module scan as the module scan requires knowing what the active profile is. @@ -111,7 +116,7 @@ protected function getProfilePath($profile) { // @todo Remove as part of https://www.drupal.org/node/2186491. drupal_get_filename('profile', $profile_name, $extension->getPathname()); } - $this->scan_cache = TRUE; + $this->scanCache = TRUE; } return drupal_get_path('profile', $profile); } @@ -122,24 +127,24 @@ protected function getProfilePath($profile) { public function getProfileInfo($profile) { // Even though info_parser caches the info array, we need to also cache // this since it is recursive. - if (!isset($this->info_cache[$profile])) { + if (!isset($this->infoCache[$profile])) { // Set defaults for profile info. - $defaults = array( - 'dependencies' => array(), - 'themes' => array('stark'), + $defaults = [ + 'dependencies' => [], + 'themes' => ['stark'], 'description' => '', 'version' => NULL, 'hidden' => FALSE, 'php' => DRUPAL_MINIMUM_PHP, - ); + ]; $profile_path = $this->getProfilePath($profile); $profile_file = $profile_path . "/$profile.info.yml"; $info = $this->infoParser->parse($profile_file); $info += $defaults; - $profile_list = array(); + $profile_list = []; // Get the base profile dependencies. $base_profile_name = ProfileHandler::getProfileBaseName($info); if ($base_profile_name) { @@ -148,12 +153,11 @@ public function getProfileInfo($profile) { // Ensure all dependencies are cleanly merged. $info['dependencies'] = array_merge($info['dependencies'], $base_info['dependencies']); - // If there are dependency excludes from the base apply them now. - if (!empty($info['base profile']['excluded_dependencies'])) { - $info['dependencies'] = array_diff($info['dependencies'], $info['base profile']['excluded_dependencies']); - } + $info['base profile'] += ['excluded_dependencies' => []]; + // Apply excluded dependencies. + $info['dependencies'] = array_diff($info['dependencies'], $info['base profile']['excluded_dependencies']); // Ensure there's no circular dependency. - $info['dependencies'] = array_diff($info['dependencies'], array($profile)); + $info['dependencies'] = array_diff($info['dependencies'], [$profile]); } $profile_list[$profile] = $profile; $info['profile_list'] = $profile_list; @@ -167,24 +171,27 @@ public function getProfileInfo($profile) { // otherwise in the .info.yml file. $info['hidden'] = isset($info['hidden']) ? $info['hidden'] : TRUE; - $this->info_cache[$profile] = $info; + $this->infoCache[$profile] = $info; } - return $this->info_cache[$profile]; + return $this->infoCache[$profile]; } /** * {@inheritdoc} */ public function setProfileInfo($profile, $info) { - $this->info_cache[$profile] = $info; + $this->infoCache[$profile] = $info; // Also unset the cached profile extension so the updated info will // be picked up. unset($this->cache[$profile]); } + /** + * {@inheritdoc} + */ public function clearProfileCache() { - $this->cache = array(); - $this->info_cache = array(); + $this->cache = []; + $this->infoCache = []; } /** @@ -238,7 +245,7 @@ public function getProfiles($profile = NULL) { $profile = drupal_get_profile(); } if (!isset($this->cache[$profile])) { - $profiles = array(); + $profiles = []; // Check if a valid profile name was given. if (!empty($profile)) { $list = $this->getProfileList($profile); @@ -247,7 +254,7 @@ public function getProfiles($profile = NULL) { $weight = 1000; // Loop through profile list and create Extension objects. - $profiles = array(); + $profiles = []; foreach ($list as $profile_name) { $extension = $this->getProfileExtension($profile_name); $extension->weight = $weight; @@ -265,17 +272,15 @@ public function getProfiles($profile = NULL) { */ public function selectDistribution($profile_list) { // First, find all profiles marked as distributions - $distributions = array(); - foreach ($profile_list as $profile) { - $profile_name = is_object($profile) ? $profile->getName() : $profile; + $distributions = []; + foreach ($profile_list as $profile_name) { $profile_info = $this->getProfileInfo($profile_name); if (!empty($profile_info['distribution'])) { - $distributions[$profile_name] = $profile; + $distributions[$profile_name] = $profile_name; } } // Remove any base profiles. - foreach ($profile_list as $profile) { - $profile_name = is_object($profile) ? $profile->getName() : $profile; + foreach ($profile_list as $profile_name) { $profile_info = $this->getProfileInfo($profile_name); if ($base_profile = self::getProfileBaseName($profile_info)) { unset($distributions[$base_profile]); @@ -287,6 +292,20 @@ public function selectDistribution($profile_list) { /** * {@inheritdoc} */ + public function selectDistributionExtension($profile_extension_list) { + $profile_list = array_map(function (Extension $profile) { + return $profile->getName(); + }, $profile_extension_list); + + if ($distribution_name = $this->selectDistribution($profile_list)) { + return $profile_extension_list[$distribution_name]; + } + return NULL; + } + + /** + * {@inheritdoc} + */ static public function getProfileBaseName($info) { return !empty($info['base profile']['name']) ? $info['base profile']['name'] diff --git a/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php b/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php index 1cb6824..c5301dd 100644 --- a/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ProfileHandlerInterface.php @@ -3,7 +3,7 @@ namespace Drupal\Core\Extension; /** - * Defines an interface for listing and managing installation profiles. + * Lists and manages installation profiles. */ interface ProfileHandlerInterface { @@ -19,7 +19,7 @@ * 4) Add the $info['profile_list'] list of dependent profiles. * * @param string $profile - * Name of profile. + * The name of profile. * * @return array * The processed $info array. @@ -33,7 +33,7 @@ public function getProfileInfo($profile); * This is used for testing. * * @param string $profile - * Name of profile. + * THe name of profile. * @param array $info * The info array to be set. */ @@ -44,15 +44,15 @@ public function setProfileInfo($profile, $info); */ public function clearProfileCache(); - /** + /** * Returns a list of dependent installation profiles. * * @param string $profile - * Name of profile. If none is specified, use the current profile. + * Name of profile. If none is specified, use the current profile. * * @return \Drupal\Core\Extension\Extension[] - * An associative array of Extension objects, keyed by profile name - * in descending order of their dependencies. + * An associative array of Extension objects, keyed by profile name in + * descending order of their dependencies. * (parent profiles first, main profile last) */ public function getProfiles($profile = NULL); @@ -64,16 +64,30 @@ public function getProfiles($profile = NULL); * If there is an inherited profile marked as a distribution, select it over * its base profile. * - * @param array $profile_list - * List of profiles to search. Can be either an array of extensions or - * an array of profile name strings. - * @return \Drupal\Core\Extension\Extension or string - * The selected distribution, or NULL if none is found. The type returned - * is the same as the type passed in $profile_list. + * @param string[] $profile_list + * List of profiles names to search. + * + * @return string|null + * The selected distribution, or NULL if none is found. */ public function selectDistribution($profile_list); /** + * Select the install distribution from the list of profiles. + * + * If there are multiple profiles marked as distributions, select the first. + * If there is an inherited profile marked as a distribution, select it over + * its base profile. + * + * @param \Drupal\Core\Extension\Extension[] $profile_extension_list + * List of profile extensions to search. + * + * @return \Drupal\Core\Extension\Extension|null + * The selected distribution, or NULL if none is found. + */ + public function selectDistributionExtension($profile_extension_list); + + /** * Return the name of a profile from its info. * * The info array can reference a "base profile" in two ways: diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 0a79126..811c45f 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -963,6 +963,7 @@ function _system_rebuild_module_data() { $modules = $listing->scan('module'); // Find profiles. + /** @var \Drupal\Core\Extension\ProfileHandlerInterface $profile_handler */ $profile_handler = \Drupal::service('profile_handler'); $modules = array_merge($modules, $profile_handler->getProfiles()); diff --git a/core/profiles/testing_inherited/src/Tests/InheritedProfileTest.php b/core/profiles/testing_inherited/tests/src/Functional/InheritedProfileTest.php similarity index 56% rename from core/profiles/testing_inherited/src/Tests/InheritedProfileTest.php rename to core/profiles/testing_inherited/tests/src/Functional/InheritedProfileTest.php index 1a8c929..165779b 100644 --- a/core/profiles/testing_inherited/src/Tests/InheritedProfileTest.php +++ b/core/profiles/testing_inherited/tests/src/Functional/InheritedProfileTest.php @@ -2,15 +2,18 @@ namespace Drupal\testing_inherited\Tests; -use Drupal\simpletest\WebTestBase; +use Drupal\Tests\BrowserTestBase; /** - * Tests Inherited profiles. + * Tests inherited profiles. * * @group profiles */ -class InheritedProfileTest extends WebTestBase { +class InheritedProfileTest extends BrowserTestBase { + /** + * {@inheritdoc} + */ protected $profile = 'testing_inherited'; /** @@ -19,9 +22,10 @@ class InheritedProfileTest extends WebTestBase { function testInheritedProfile() { $this->drupalGet(''); // Check the login block is present. - $this->assertLink(t('Create new account')); - $this->assertResponse(200); - // Check that proper modules were enabled or not. + $this->assertSession()->linkExists(t('Create new account')); + $this->assertSession()->statusCodeEquals(200); + + // Check the excluded_dependencies flag on installation profiles. $this->assertTrue(\Drupal::moduleHandler()->moduleExists('config')); $this->assertFalse(\Drupal::moduleHandler()->moduleExists('dblog')); } diff --git a/core/tests/Drupal/KernelTests/Core/Extension/ProfileHandlerTest.php b/core/tests/Drupal/KernelTests/Core/Extension/ProfileHandlerTest.php index 5ec0b0e..6c63ee6 100644 --- a/core/tests/Drupal/KernelTests/Core/Extension/ProfileHandlerTest.php +++ b/core/tests/Drupal/KernelTests/Core/Extension/ProfileHandlerTest.php @@ -41,10 +41,10 @@ public function testGetProfileInfo() { $this->assertNotEmpty($info['profile_list']); $profile_list = $info['profile_list']; // Testing order of profile list. - $this->assertEquals($profile_list, array( + $this->assertEquals($profile_list, [ 'minimal' => 'minimal', 'testing_inherited' => 'testing_inherited' - )); + ]); } /** @@ -71,14 +71,13 @@ public function testGetProfiles() { } /** - * Tests getting profile dependency list. - * * @covers ::selectDistribution * @covers ::setProfileInfo */ public function testSelectDistribution() { + /** @var \Drupal\Core\Extension\ProfileHandler $profile_handler */ $profile_handler = $this->container->get('profile_handler'); - $profiles = array('minimal', 'testing_inherited'); + $profiles = ['minimal', 'testing_inherited']; $base_info = $profile_handler->getProfileInfo('minimal'); $profile_info = $profile_handler->getProfileInfo('testing_inherited'); @@ -101,4 +100,34 @@ public function testSelectDistribution() { $this->assertEquals($distribution, 'testing_inherited'); } + /** + * @covers ::selectDistribution + * @covers ::setProfileInfo + */ + public function testSelectDistributionExtension() { + /** @var \Drupal\Core\Extension\ProfileHandler $profile_handler */ + $profile_handler = $this->container->get('profile_handler'); + $profiles = $profile_handler->getProfiles('testing_inherited'); + $base_info = $profile_handler->getProfileInfo('minimal'); + $profile_info = $profile_handler->getProfileInfo('testing_inherited'); + + // Neither profile has distribution set + $distribution = $profile_handler->selectDistributionExtension($profiles); + $this->assertEmpty($distribution, 'No distribution should be selected'); + + // Set base profile distribution + $base_info['distribution']['name'] = 'Minimal'; + $profile_handler->setProfileInfo('minimal', $base_info); + // Base profile distribution should not be selected + $distribution = $profile_handler->selectDistributionExtension($profiles); + $this->assertEmpty($distribution, 'Base profile distribution should not be selected'); + + // Set main profile distribution + $profile_info['distribution']['name'] = 'Testing Inherited'; + $profile_handler->setProfileInfo('testing_inherited', $profile_info); + // Main profile distribution should be selected + $distribution = $profile_handler->selectDistributionExtension($profiles); + $this->assertEquals($distribution, $profiles['testing_inherited']); + } + }