While reviewing #2256257-11: Move token seed in SessionManager in isSessionObsolete() and regenerate() to the MetadataBag I saw this ugly messing of object mocking for an object that clearly didn't need it. While investigating I found it was because of the complicated way we've set up our Settings object.

In this patch we separate the settings storage from the Settings class and create a separate class to handle it. The storage class can then be instantiated on its own and injected into a constructor during testing.

This retains all the read-only aspects of the settings system. The container won't allow it to be over-ridden and the static methods on Settings will behave in the exact same manner and retain the same interface.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

neclimdul’s picture

Oh, #2250491: Remove duplicate methods from Settings class is sort of relevant to this issue. We're sort of adding those methods back on a separate class but with a clear purpose.

+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php
@@ -23,7 +23,7 @@ class ReverseProxySubscriberUnitTest extends UnitTestCase {
-    $settings = new Settings(array());
+    $settings = new SettingsStorage(array());

This is the subtly important line. The previous line changed global state and left an artifact of the empty storage on the global Settings object. The new line just creates an object.

sun’s picture

Component: configuration system » base system
Issue tags: -Unit tests +Testing system, +PHPUnit, +DX (Developer Experience)

Makes sense to me in general. To think through this verbosely:

Since Settings are read-only, theoretically it should be safe and should not matter that the services are getting the value object injected.

The proposed construct may cause those injected objects to diverge from the object in the singleton. Special attention to serialization scenarios.

Wondering whether we can proactively prevent that somehow? Could we implement Serializable and make SettingsStorage::unserialize() instantiate itself by calling Settings::getStorage()?


Aside from the above, minor: Not sure whether SettingsStorage is the best name; it's not really a storage, but a fancy hashmap / custom ArrayObject-alike value object. How about SettingsBag?

damiankloip’s picture

Generally I think this looks like a good plan. Gives us a bit more flexibility. We started using Settings:get() directly in code, but this could enable passing it back as param where it makes sense. This can be a follow up though, for sure.

Would be interested to see what sun thinks about this. I know he wanted to do something with the current state of settings. x post :)

Couple of nits:

  1. +++ b/core/lib/Drupal/Core/Site/SettingsStorage.php
    @@ -0,0 +1,58 @@
    +   * Constructor.
    

    I am fine with that. Not sure if we officially have to use 'Constructs a..' ?

  2. +++ b/core/lib/Drupal/Core/Site/SettingsStorage.php
    @@ -0,0 +1,58 @@
    +   *   The value of the setting, the provided default if the value is not set.
    

    I think we can squeeze an ", or .." on that line too?

damiankloip’s picture

Status: Needs review » Needs work

The last submitted patch, SettingsStorage.patch, failed testing.

sun’s picture

sun’s picture

Several issues also discussed that our ideal end goal should actually be to remove the singleton pattern from Settings — at which point the Settings class itself would turn into the bag / value object.

In essence, the idea would be:

core/lib/Drupal/Core/DrupalKernel.php
@@
-  public function __construct($environment, ClassLoader $class_loader, $allow_dumping = TRUE) {
+  public function __construct($environment, ClassLoader $class_loader, SettingsBag $settings, $allow_dumping = TRUE) {
+    $this->settings = $settings;

@@
   protected function attachSynthetic(ContainerInterface $container) {
+    $container->set('settings', $this->settings);

Now that I think of it, that approach would actually inject this new SettingsBag value object, so this patch here seems to cleanly contribute towards that goal. :-)

neclimdul’s picture

re: #3 I think the serialization issue you linked could probably better address the serialization problem. I think you may have identified something that may have been overlooked there.

re: #4 I think some of those are(or should be) ports of old methods. Issues I've been watching have services taking Settings as a dependency. As discussed in IRC, Settings::getHashSalt() is a bit of a thorn in this model. I'll take a look at that documentation when looking at the failure.

re: #7 Agreed, this definitely moving toward that goal.

sun’s picture

@neclimdul: Can you re-roll to address #3 + potentially rename to SettingsBag?

Looks like this is a quick win.

neclimdul’s picture

Status: Needs work » Needs review
FileSize
17.51 KB
1.28 KB

Agreed, sorry for the delay. Busy weekend then got sick. re-roll, rename, and doc changes in the interdiff.

Status: Needs review » Needs work

The last submitted patch, 10: settings-bag-2329817-10.patch, failed testing.

neclimdul’s picture

Status: Needs work » Needs review
FileSize
20.56 KB
5.11 KB

Fix a file I seem to have missed? Also noticed some documentation I missed on class properties.

I think there are some fixes needed to some phpunit tests still. I'm not sure why testbot isn't seeing the failures though.

Status: Needs review » Needs work

The last submitted patch, 12: settings-bag-2329817-12.patch, failed testing.

neclimdul’s picture

Status: Needs work » Needs review
FileSize
25.64 KB
5.08 KB

Don't know why some of these didn't fail in the original patch. Clearly I suspected testbot and was wrong... luck?

Anyways, fixes for tests in interdiff.

znerol’s picture

How about reusing Symfony\Component\DependencyInjection\ParameterBag\ParameterBag and [...]\FrozenParameterBag?

dawehner’s picture

@znerol
Well, sure that class would fit quite well, but it feels wrong to depend on a DependencyInjection class.

+++ b/core/lib/Drupal/Core/Site/SettingsBag.php
@@ -0,0 +1,58 @@
+/**
+ * Read only settings data object.
+ *
+ * @ingroup utility
+ */
+class SettingsBag {

Did we considered to introduce a Bag component which has no dependency at all, and settings would just use it?

neclimdul’s picture

We can't anyways. ParameterBagInterface does not have the default parameter we provide on get. Removing that would be a longer and involved process that would need additional community discussion.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jhedstrom’s picture

Version: 8.1.x-dev » 8.2.x-dev
Status: Needs review » Needs work
Issue tags: +Needs reroll

Patch no longer applies.

ashishdalvi’s picture

We will take this issue on Drupal Mumbai code sprint today.

neclimdul’s picture

Version: 8.2.x-dev » 9.x-dev

If you guys don't get to it I can easily get one done. Unfortunately since this changes the return type of getInstance() and the type of the settings service there are 2 API breaks and in its current state it can't go into 8.x.

ashishdalvi’s picture

Cleaning unwanted tags

catch’s picture

Version: 9.x-dev » 8.3.x-dev

We could add a new settings system alongside the old one, both can take from $settings in settings.php. Move usages to the new one and deprecate the old one. Ugly but not impossible in a minor release, so moving back.

Manuel Garcia’s picture

Assigned: neclimdul » Unassigned
Status: Needs work » Needs review
FileSize
14.38 KB

Rerolled against 8.3.x, please review =)

Files that had conflicts:

  • core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
  • core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
  • core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php (deleted)
  • core/tests/Drupal/Tests/Core/Cache/CacheFactoryTest.php
  • core/modules/language/src/LanguageNegotiator.php
  • core/modules/language/src/HttpKernel/PathProcessorLanguage.php
  • core/lib/Drupal/Core/Session/SessionManager.php
  • core/lib/Drupal/Core/Routing/UrlGenerator.php
  • core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php (deleted)
  • core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php

These files no longer use Drupal\Core\Site\Settings; so I've removed the changes that we were doing there:

  • core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
  • core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
  • core/modules/language/src/HttpKernel/PathProcessorLanguage.php
  • core/lib/Drupal/Core/Session/SessionManager.php
  • core/lib/Drupal/Core/Routing/UrlGenerator.php

Files that previous patch was making changes but are now deleted:

  • core/tests/Drupal/Tests/Core/EventSubscriber/ReverseProxySubscriberUnitTest.php
  • core/lib/Drupal/Core/EventSubscriber/ReverseProxySubscriber.php

Status: Needs review » Needs work

The last submitted patch, 24: add_a_settings_storage-2329817-24.patch, failed testing.

hitesh-jain’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 24: add_a_settings_storage-2329817-24.patch, failed testing.

Manuel Garcia’s picture

Here is the error that the bot found apparently:

13:51:46 PHP Catchable fatal error:  Argument 2 passed to Drupal\Core\File\FileSystem::__construct() must be an instance of Drupal\Core\Site\Settings, instance of Drupal\Core\Site\SettingsBag given in /var/www/html/core/lib/Drupal/Core/File/FileSystem.php on 
line 55
Manuel Garcia’s picture

Issue tags: -Needs reroll

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.