Hello!

While installing or deinstalling Webform, we've received the error below.
Webform assumes a 'en'-langcode, while the website we run this on does not have a 'en' langcode language.
We have 'en-INT', 'en-US', ... etc.

The problem is that the method 'setLanguage' in '\Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride' immediately assumes that an instance of the LanguageInterface has been given, while that might not always be the case.

Proposed solution
Check whether an instance of LanguageInterface has been given, and if not, take the currently active language? See the patch attached.

vagrant@XXXXXXXX:/var/www/drupalvm/web$ drush pmu webform
 [error]  Error: Call to a member function getId() on null in Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride->setLanguage() (line 95 of /var/www/drupalvm/web/modules/contrib/language_hierarchy/src/Config/LanguageHierarchyConfigFactoryOverride.php) #0 /var/www/drupalvm/web/core/modules/language/src/ConfigurableLanguageManager.php(441): Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride->setLanguage(NULL)
<strong>#1 /var/www/drupalvm/web/modules/contrib/webform/src/WebformTranslationManager.php(123): Drupal\language\ConfigurableLanguageManager->setConfigOverrideLanguage(NULL)</strong>
#2 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(1429): Drupal\webform\WebformTranslationManager->getElements(Object(Drupal\webform\Entity\Webform))
#3 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(1209): Drupal\webform\Entity\Webform->initElements()
#4 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2156): Drupal\webform\Entity\Webform->getElementsDecoded()
#5 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(534): Drupal\webform\Entity\Webform->postSave(Object(Drupal\webform\WebformEntityStorage), true)
#6 /var/www/drupalvm/web/modules/contrib/webform/src/WebformEntityStorage.php(126): Drupal\Core\Entity\EntityStorageBase->doPostSave(Object(Drupal\webform\Entity\Webform), true)
#7 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(460): Drupal\webform\WebformEntityStorage->doPostSave(Object(Drupal\webform\Entity\Webform), true)
#8 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php(263): Drupal\Core\Entity\EntityStorageBase->save(Object(Drupal\webform\Entity\Webform))
#9 /var/www/drupalvm/web/modules/contrib/webform/src/WebformEntityStorage.php(133): Drupal\Core\Config\Entity\ConfigEntityStorage->save(Object(Drupal\webform\Entity\Webform))
#10 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityBase.php(395): Drupal\webform\WebformEntityStorage->save(Object(Drupal\webform\Entity\Webform))
#11 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php(613): Drupal\Core\Entity\EntityBase->save()
#12 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2467): Drupal\Core\Config\Entity\ConfigEntityBase->save()
#13 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2894): Drupal\webform\Entity\Webform->deleteWebformHandler(Object(Drupal\webform\Plugin\WebformHandler\EmailWebformHandler))
#14 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(494): Drupal\webform\Entity\Webform->onDependencyRemoval(Array)
#15 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(357): Drupal\Core\Config\ConfigManager->callOnDependencyRemoval(Object(Drupal\webform\Entity\Webform), Array, 'module', Array)
#16 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(218): Drupal\Core\Config\ConfigManager->getConfigEntitiesToChangeOnDependencyRemoval('module', Array, false)
#17 /var/www/drupalvm/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php(431): Drupal\Core\Config\ConfigManager->uninstall('module', 'webform')
#18 /var/www/drupalvm/web/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php(91): Drupal\Core\Extension\ModuleInstaller->uninstall(Array, true)
#19 /var/www/drupalvm/vendor/drush/drush/src/Drupal/Commands/pm/PmCommands.php(107): Drupal\Core\ProxyClass\Extension\ModuleInstaller->uninstall(Array, true)
#20 [internal function]: Drush\Drupal\Commands\pm\PmCommands->uninstall(Array, Array)
#21 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array(Array, Array)
#22 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback(Array, Object(Consolidation\AnnotatedCommand\CommandData))
#23 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(178): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter(Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#24 /var/www/drupalvm/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(302): Consolidation\AnnotatedCommand\CommandProcessor->process(Object(Symfony\Component\Console\Output\ConsoleOutput), Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#25 /var/www/drupalvm/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#26 /var/www/drupalvm/vendor/symfony/console/Application.php(1005): Symfony\Component\Console\Command\Command->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#27 /var/www/drupalvm/vendor/symfony/console/Application.php(255): Symfony\Component\Console\Application->doRunCommand(Object(Consolidation\AnnotatedCommand\AnnotatedCommand), Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#28 /var/www/drupalvm/vendor/symfony/console/Application.php(148): Symfony\Component\Console\Application->doRun(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#29 /var/www/drupalvm/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#30 /var/www/drupalvm/vendor/drush/drush/src/Runtime/Runtime.php(49): Drush\Runtime\Runtime->doRun(Array, Object(Symfony\Component\Console\Output\ConsoleOutput))
#31 /var/www/drupalvm/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run(Array)
#32 /var/www/drupalvm/vendor/drush/drush/includes/preflight.inc(18): require('/var/www/drupal...')
#33 phar:///usr/local/bin/drush/bin/drush.php(141): drush_main()
#34 /usr/local/bin/drush(10): require('phar:///usr/loc...')
#35 {main}. 
Error: Call to a member function getId() on null in /var/www/drupalvm/web/modules/contrib/language_hierarchy/src/Config/LanguageHierarchyConfigFactoryOverride.php on line 95 #0 /var/www/drupalvm/web/core/modules/language/src/ConfigurableLanguageManager.php(441): Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride->setLanguage(NULL)
#1 /var/www/drupalvm/web/modules/contrib/webform/src/WebformTranslationManager.php(123): Drupal\language\ConfigurableLanguageManager->setConfigOverrideLanguage(NULL)
#2 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(1429): Drupal\webform\WebformTranslationManager->getElements(Object(Drupal\webform\Entity\Webform))
#3 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(1209): Drupal\webform\Entity\Webform->initElements()
#4 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2156): Drupal\webform\Entity\Webform->getElementsDecoded()
#5 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(534): Drupal\webform\Entity\Webform->postSave(Object(Drupal\webform\WebformEntityStorage), true)
#6 /var/www/drupalvm/web/modules/contrib/webform/src/WebformEntityStorage.php(126): Drupal\Core\Entity\EntityStorageBase->doPostSave(Object(Drupal\webform\Entity\Webform), true)
#7 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityStorageBase.php(460): Drupal\webform\WebformEntityStorage->doPostSave(Object(Drupal\webform\Entity\Webform), true)
#8 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php(263): Drupal\Core\Entity\EntityStorageBase->save(Object(Drupal\webform\Entity\Webform))
#9 /var/www/drupalvm/web/modules/contrib/webform/src/WebformEntityStorage.php(133): Drupal\Core\Config\Entity\ConfigEntityStorage->save(Object(Drupal\webform\Entity\Webform))
#10 /var/www/drupalvm/web/core/lib/Drupal/Core/Entity/EntityBase.php(395): Drupal\webform\WebformEntityStorage->save(Object(Drupal\webform\Entity\Webform))
#11 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php(613): Drupal\Core\Entity\EntityBase->save()
#12 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2467): Drupal\Core\Config\Entity\ConfigEntityBase->save()
#13 /var/www/drupalvm/web/modules/contrib/webform/src/Entity/Webform.php(2894): Drupal\webform\Entity\Webform->deleteWebformHandler(Object(Drupal\webform\Plugin\WebformHandler\EmailWebformHandler))
#14 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(494): Drupal\webform\Entity\Webform->onDependencyRemoval(Array)
#15 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(357): Drupal\Core\Config\ConfigManager->callOnDependencyRemoval(Object(Drupal\webform\Entity\Webform), Array, 'module', Array)
#16 /var/www/drupalvm/web/core/lib/Drupal/Core/Config/ConfigManager.php(218): Drupal\Core\Config\ConfigManager->getConfigEntitiesToChangeOnDependencyRemoval('module', Array, false)
#17 /var/www/drupalvm/web/core/lib/Drupal/Core/Extension/ModuleInstaller.php(431): Drupal\Core\Config\ConfigManager->uninstall('module', 'webform')
#18 /var/www/drupalvm/web/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php(91): Drupal\Core\Extension\ModuleInstaller->uninstall(Array, true)
#19 /var/www/drupalvm/vendor/drush/drush/src/Drupal/Commands/pm/PmCommands.php(107): Drupal\Core\ProxyClass\Extension\ModuleInstaller->uninstall(Array, true)
#20 [internal function]: Drush\Drupal\Commands\pm\PmCommands->uninstall(Array, Array)
#21 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array(Array, Array)
#22 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback(Array, Object(Consolidation\AnnotatedCommand\CommandData))
#23 /var/www/drupalvm/vendor/consolidation/annotated-command/src/CommandProcessor.php(178): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter(Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#24 /var/www/drupalvm/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(302): Consolidation\AnnotatedCommand\CommandProcessor->process(Object(Symfony\Component\Console\Output\ConsoleOutput), Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#25 /var/www/drupalvm/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#26 /var/www/drupalvm/vendor/symfony/console/Application.php(1005): Symfony\Component\Console\Command\Command->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#27 /var/www/drupalvm/vendor/symfony/console/Application.php(255): Symfony\Component\Console\Application->doRunCommand(Object(Consolidation\AnnotatedCommand\AnnotatedCommand), Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#28 /var/www/drupalvm/vendor/symfony/console/Application.php(148): Symfony\Component\Console\Application->doRun(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#29 /var/www/drupalvm/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#30 /var/www/drupalvm/vendor/drush/drush/src/Runtime/Runtime.php(49): Drush\Runtime\Runtime->doRun(Array, Object(Symfony\Component\Console\Output\ConsoleOutput))
#31 /var/www/drupalvm/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run(Array)
#32 /var/www/drupalvm/vendor/drush/drush/includes/preflight.inc(18): require('/var/www/drupal...')
#33 phar:///usr/local/bin/drush/bin/drush.php(141): drush_main()
#34 /usr/local/bin/drush(10): require('phar:///usr/loc...')
#35 {main}
Error: Call to a member function getId() on null in Drupal\language_hierarchy\Config\LanguageHierarchyConfigFactoryOverride->setLanguage() (line 95 of /var/www/drupalvm/web/modules/contrib/language_hierarchy/src/Config/LanguageHierarchyConfigFactoryOverride.php).
 [warning] Drush command terminated abnormally.

Comments

RandalV created an issue. See original summary.

james.williams’s picture

Status: Active » Needs work

Good spot! Going by core's LanguageConfigFactoryOverride class, it defaults to using the site default language rather than the current language though. And we should use dependency injection where possible please. Note that it's also quite likely that the language manager service may not always be available when setLanguage() is called, so additional care needs to be taken to only use things that are definitely available.

randalv’s picture

Hi James!

Thanks for the reply.
You're right, I made the patch in a jiffy to get our project on the road again, forgot to refactor it before posting here.
Perhaps using ->getLanguage() instead could work? Since the default language is set upon constructing the parent class.

e.g:

  /**
   * {@inheritdoc}
   */
  public function setLanguage(LanguageInterface $language = NULL) {
    if (!$language instanceof LanguageInterface) {
      $language = $this->getLanguage();
    }
    $this->fallbackChain = $this->getFallbackChainFromConfigEntities($language->getId());
    return parent::setLanguage($language);
  }
james.williams’s picture

I'm not so sure. The parent class from core would happily override $this->language with NULL, which effectively resets any previous value it had. We should probably do the same, so rather than just using the existing language, perhaps we should 'reset' back to the initial state too - whether that means putting $this->fallbackChain back to NULL too, or deliberately going back to the language that would have been set in the constructor, as opposed to whatever was previously in $this->language?

To be honest, I don't know what passing NULL in really means at an API level. Does it mean clear any overrides? Use those for the default language? Something else?
Can we find any places in core where NULL would be passed, or explanations from webform about why it might pass NULL? Looking at the webform code, it would pass NULL when the language code being used doesn't actually match an enabled language, perhaps.

Webform assumes a 'en'-langcode, while the website we run this on does not have a 'en' langcode language.

I'm beginning to think that regardless of any required fix in language hierarchy (which I'm happy to make, if we can find the right fix!), the 'real' bug lies in this assumption that webform may have made, if true?

randalv’s picture

Hi James!

You are definitely right that Webform assuming a certain langcode is not correct in the first place, but I do feel like the setLanguage method should allow for a NULL value considering the parent class from core does as well, so we can't straight up call ->getId() on the $language parameter without checking whether it is an instance of the LanguageInterface.

I'm honestly not 100% sure what the right approach here would be, I'll help and look further into it when I have some more time off my current project :-)
To be continued!

etroid’s picture

Was running into the same issue when installing a D8 instance from existing config. Similar to RandalV we don't have `en` as the default language, but use custom locales such as `en-US` etc... Because of this and potentially the webform module, we are running into errors in `getFallbackChainFromConfigEntities`where the $langcode is NULL. For the time being I just put a bandaid on the issue using the refactor below which does allow me to finish installing Drupal 8. Of course this does not actually solve the underlying problem.

  protected function getFallbackChainFromConfigEntities($langcode) {
    /** @var \Drupal\language\ConfigurableLanguageInterface $language_config */
    $language_config = \Drupal::entityTypeManager()
      ->getStorage('configurable_language')
      ->load($langcode);
    if (isset($language_config)) {
      $fallbacks = language_hierarchy_get_ancestors($language_config);

      return array_keys($fallbacks);
    }

    return [];
  }
james.williams’s picture

Version: 8.x-1.0-rc1 » 2.x-dev
Status: Needs work » Needs review
StatusFileSize
new720 bytes

@Etroid a fix intended for #3106841: 504 error on deleting parent language has been committed to the 2.x-dev branch, which is similar to the fix in your snippet there. But yes, we still need to resolve the underlying issue with setLanguage(). Here's my proposal - to allow setting the language to NULL, just as core does, and resetting the computed fallback chain. I don't believe using the current, or default, language at the time of calling setLanguage() would be the correct thing to do, going by how LanguageConfigFactoryOverride looks.

Status: Needs review » Needs work

The last submitted patch, 7: language_hierarchy-check_interface-3120219-7.patch, failed testing. View results

james.williams’s picture

Status: Needs work » Needs review

Why are patches being treated as fails when they don't fail?! (Saw this on another ticket recently.)

james.williams’s picture

james.williams’s picture

Status: Needs review » Reviewed & tested by the community

I'm going to include this in the next release, as I believe it's definitely better than the current code, whatever happens. Could do with confirmation that it's actually the right fix though, then I'm happy to get a follow-up in if necessary.

james.williams’s picture

Status: Reviewed & tested by the community » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.