Problem/Motivation

It looks like we broke a fair number of contributed modules by introducing the \Drupal\Core\Form\ConfigFormBase::$typedConfigManager property in #3364506: Add optional validation constraint support to ConfigFormBase because some contributed modules already have this property in their config forms but without the typehint. However, it looks like some modules have it without the typehint and some with.

Proposed resolution

Should we try changing the property name to something that is less likely to conflict with the base class? We could also trigger deprecation if <code>\Drupal\Core\Form\ConfigFormBase::$typedConfigManager exists so that contrib knows to remove the duplicate properties.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Issue fork drupal-3394197

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

lauriii created an issue. See original summary.

wim leers’s picture

That search yielded mostly false positives. Also restricting to *Form.php produces far fewer: 49 instead of 588.

Actually, even that produces a lot of false positives.

If we also require ConfigFormBase to be present, then it drops to 15 matches.

That … makes it a bit more unclear.

I propose:

  1. Rename core's use (prefix with an underscore?).
  2. Trigger a deprecation error that warns this will no longer be allowed in Drupal 11.
  3. … but we'll need to be careful to avoid the opposite problem when we eventually make this a reality, to avoid subclasses relying on the renamed property instead of intended property … 😬 So … not entirely sure actually 😅
lauriii’s picture

Issue summary: View changes
StatusFileSize
new2.55 KB

That seems a bit less concerning 👍 Updated the smallest list to the issue summary.

Could we mark the new property private and deprecate warning if there's existing property that is not the right type? Something along the lines of this.

wim leers’s picture

I think that is a net improvement! 👍

moshe weitzman’s picture

Devel also broke with this change and it is not listed in your report so not sure the report is super accurate - https://gitlab.com/drupalspoons/devel/-/issues/483. Devel code is also on git.drupal.org so it is eligible.

lauriii’s picture

Status: Active » Needs review

I cleaned up the proposed change from #3 and pushed it to a MR.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Marking so it can still make 10.2 window, and if it breaks contrib modules.

But should a follow up be opened for test coverage?

wim leers’s picture

@smustgrave How could we test this?

@lauriii What about the concern in #2.3?

lauriii’s picture

@Wim Leers I changed the property to a private property to try to address that. It's not perfect because you need to override the __construct if you want to use it. Not sure if there is a perfect solution for this other than trying to fix all of contrib.

You could install one of the contributed modules to see if there's a fatal error. I used Layout Paragraphs myself.

wim leers’s picture

Title: The new property \Drupal\Core\Form\ConfigFormBase::$typedConfigManager breaks some contrib modules » The new property \Drupal\Core\Form\ConfigFormBase::$typedConfigManager conflicts with some contrib modules
Status: Reviewed & tested by the community » Needs work

1. devel

AFAICT Devel's form is just incorrect:

<?php

class SomeService {
    public string $something;
}

// before, in 10.1
class BaseOld {
    protected SomeService $test;
    public function __construct(?SomeService $test = NULL) {
        if ($test === NULL) {
            $test = new SomeService();
            $test->something = 'fallback';
        }
        $this->test = $test;
    }
    public function printTest(): void {
        print __CLASS__ . "\t" . $this->test->something . "\n";
    }
}
// after
class BaseNew {
    private SomeService $test;
    public function __construct(?SomeService $test = NULL) {
        if ($test === NULL) {
            $test = new SomeService();
            $test->something = 'fallback';
        }
        $this->test = $test;
    }
    public function printTest(): void {
        print __CLASS__ . "\t" . $this->test->something . "\n";
    }
}

//NOTE: IDENTICAL!
// class FooOld extends BaseOld {
//     protected $test;
//     public function __construct() {
//         parent::__construct();
//         $this->test = new SomeService();
//         $this->test->something = 'subclass';
//     }
// }
class FooNew extends BaseNew {
    protected $test;
    public function __construct() {
        parent::__construct();
        $this->test = new SomeService();
        $this->test->something = 'subclass';
    }
}

// $f = new FooOld();
// $f->printTest();
$f = new FooNew();
$f->printTest();

— https://3v4l.org/m6AJL

longwave’s picture

So in 10.1, subclasses of ConfigFormBase did not strictly require the parent constructor to be called, because there is a helper getter for the config factory in FormBase:

  protected function configFactory() {
    if (!$this->configFactory) {
      $this->configFactory = $this->container()->get('config.factory');
    }
    return $this->configFactory;
  }

Wonder if we should add a similar helper for the typed config manager service?

Also makes me wonder if forms should use setter injection for base dependencies (config factory, typed config manager, anything new we want to add in the future) and constructor injection for specific dependencies for individual forms?

wim leers’s picture

Status: Needs work » Needs review

I like that proposal, @longwave. It's pragmatic, fixes the current disruption in 10.2.0-beta1 and prevents future disruption.

Marking Needs review to get +1 from either (and hopefully both) @catch and @lauriii.

catch’s picture

Yeah that sounds worth doing to me.

longwave’s picture

Title: The new property \Drupal\Core\Form\ConfigFormBase::$typedConfigManager conflicts with some contrib modules » [regression] The new property \Drupal\Core\Form\ConfigFormBase::$typedConfigManager conflicts with some contrib modules
Status: Needs review » Needs work

As this is a regression for contrib we should try to resolve it before 10.2.0-rc1 (this week) if possible.

phenaproxima’s picture

Status: Needs work » Needs review

Added the helpful getter method, and changed the constructor deprecation so that it allows anything, but issues a deprecation if you pass in something that isn't TypedConfigManagerInterface.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.2 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

wim leers’s picture

Status: Needs work » Reviewed & tested by the community

No remarks.

longwave’s picture

Version: 11.x-dev » 10.2.x-dev
Status: Reviewed & tested by the community » Fixed

This should solve the problem for contrib and we can clean it up further in 11.x. Getting this in now so we fix the regression in 10.2.0-rc1.

Committed and pushed a9865eb372 to 11.x and 1142669363 to 10.2.x. Thanks!

Will also update and publish the change record.

  • longwave committed 11426693 on 10.2.x
    Issue #3394197 by lauriii, phenaproxima, Wim Leers, longwave, smustgrave...

  • longwave committed a9865eb3 on 11.x
    Issue #3394197 by lauriii, phenaproxima, Wim Leers, longwave, smustgrave...
wim leers’s picture

CR updated 👍

moshe weitzman’s picture

Thanks for the fix.

FYI I just changed Devel's create() method so it avoids this sort of problem and avoids a PHPStan complaint as well - https://gitlab.com/drupalspoons/devel/-/merge_requests/164.

wim leers’s picture

👍

Status: Fixed » Closed (fixed)

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