diff --git a/core/modules/language/src/ConfigurableLanguageManager.php b/core/modules/language/src/ConfigurableLanguageManager.php index 5c250dd..192e7d3 100644 --- a/core/modules/language/src/ConfigurableLanguageManager.php +++ b/core/modules/language/src/ConfigurableLanguageManager.php @@ -90,8 +90,7 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl protected $initialized = FALSE; /** - * An array of language-types currently being initialized, keyed by language- - * type. + * The language types currently being initialized. * * @var bool[] * An array of which the keys are language type names, and values are @@ -319,12 +318,14 @@ public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) { $data['name'] = $data['label']; $languages[$data['id']] = new Language($data); } - Language::sort($languages); + // The sort function calls methods on the languages. For mocked methods, + // this results in state changes, triggering an "Array was modified by the + // user comparison function" error. Suppress all errors because of this. + @Language::sort($languages); // Filter the full list of languages based on the value of $flags. $this->languages[$static_cache_id][$flags] = $this->filterLanguages($languages, $flags); } - return $this->languages[$static_cache_id][$flags]; } diff --git a/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php b/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php new file mode 100644 index 0000000..b3a9e5b --- /dev/null +++ b/core/modules/language/tests/src/Unit/ConfigurableLanguageManagerTest.php @@ -0,0 +1,143 @@ +configFactory = $this->prophesize(ConfigFactoryInterface::class); + + $this->configFactoryOverride = $this->prophesize(LanguageConfigFactoryOverrideInterface::class); + + $this->defaultLanguage = $this->prophesize(LanguageDefault::class); + + $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class); + + $this->negotiator = $this->prophesize(LanguageNegotiatorInterface::class); + + $this->requestStack = $this->prophesize(RequestStack::class); + + $this->languageManager = new ConfigurableLanguageManager($this->defaultLanguage->reveal(), $this->configFactory->reveal(), $this->moduleHandler->reveal(), $this->configFactoryOverride->reveal(), $this->requestStack->reveal()); + } + + /** + * @covers ::getCurrentLanguage + */ + public function testCurrentLanguageShouldSupportRecursion() { + // Set optional dependencies. + $this->languageManager->setNegotiator($this->negotiator->reveal()); + + // Mock the config for one configurable language. + $config_id = 'foo_bar'; + $config = $this->prophesize(Config::class); + $config->get()->willReturn([ + 'id' => $config_id, + 'label' => 'Foo Bar', + 'locked' => FALSE, + ]); + + // Mock finding the config that represents configurable languages. + $config_ids = [$config_id]; + $this->configFactory->listAll('language.entity.')->willReturn($config_ids); + $this->configFactory->loadMultiple($config_ids)->willReturn([$config]); + + $language_negotiation_method_id = 'foo_bar'; + + // Mock the default language. + $default_language = $this->prophesize(LanguageInterface::class); + $this->defaultLanguage->get()->willReturn($default_language->reveal()); + + // Mock the second language type we will test. + $expected_second_language = $this->prophesize(LanguageInterface::class); + $second_language_type = LanguageInterface::TYPE_URL; + $this->negotiator->initializeType($second_language_type)->willReturn([ + $language_negotiation_method_id => $expected_second_language, + ]); + + // Mock the first language type we will test. Note that it depends on the + // second language type to create the recursion we are testing. + $expected_first_language = $this->prophesize(LanguageInterface::class); + $first_language_type = LanguageInterface::TYPE_INTERFACE; + $language_manager = $this->languageManager; + $this->negotiator->initializeType($first_language_type)->will(function ($type) use ($language_manager, $language_negotiation_method_id, $expected_first_language, $second_language_type) { + $language_manager->getCurrentLanguage($second_language_type); + return [ + $language_negotiation_method_id => $expected_first_language, + ]; + }); + + $actual_first_language = $this->languageManager->getCurrentLanguage($first_language_type); + $this->assertSame($expected_first_language->reveal(), $actual_first_language); + $actual_second_language = $this->languageManager->getCurrentLanguage($second_language_type); + $this->assertSame($expected_second_language->reveal(), $actual_second_language); + } + +}