Problem/Motivation
As the module evolves, we can expect new services to be added, service dependencies to change, etc.
The "classic" way to inject dependencies into a class is to use arguments to the constructor (i.e.: public function __construct($dependency1) { $this->dependency1 = $dependency1; }. But if you want to add a new dependency, or delete a dependency, then you have to change the function signature of the constructor (which is often public).
Technically, Drupal core's backwards-compatibility policy considers service object constructors to be @internal; however, in practice, changing the signature of service object constructors can occasionally cause fatal errors after a module upgrade, which isn't ideal.
Fortunately, PHP provides an alternate way to inject dependencies that doesn't involve changing function signatures, and thus avoids fatal errors on module upgrades...
class Foo {
/* Omit the constructor, i.e.: to inherit it from its base class */
public static function create(ContainerInterface $container) {
$instance = new static():
$instance->dependency1 = $container->get('dependency1');
return $instance;
}
}
... to distinguish these from the classic-style of dependency injection, this issue will refer to this as the "new-style of dependency injection".
At time-of-writing, the 2.x branch of structure_sync has 4 instances of classic-style dependency injection:
- \Drupal\structure_sync\Controller\MenuLinksController::__construct()
- \Drupal\structure_sync\Form\BlocksSyncForm::__construct()
- \Drupal\structure_sync\Form\MenuSyncForm::__construct()
- \Drupal\structure_sync\Form\TaxonomiesSyncForm::__construct()
There is talk of creating a 3.0.x branch in #3517435: Drop support for D8, D9, D10.0-10.3.
We can't change constructor signatures in the 2.x branch; but we can prepare to change the signatures in the 3.0.x branch by deprecating the use of classic-style dependency injection.
Proposed resolution
For the 4 services listed above...
- start using the new-style dependency injection for those services; and;
- stop doing anything with the parameters passed into those constructors, and mark those parameters as deprecated
... this will allow us to remove those constructors in a 3.0.x branch.
Remaining tasks
Write a merge request to start using new-style dependency injection and deprecate classic-style dependency injection in the 2.x branchReview and feedback- by @spiderman in #4RTBC and feedback- skipped; maintainers worked on this issueCommitFile a follow-up ticket for the 2.x branch to deprecate the constructors in BlocksController, TaxonomiesController, and (soon to be) MenuLinksController - by being consistent about how we refer to $this->config, and $this->entityTypeManager, we could get rid of the rest of the code in those controllers- #3519479: Deprecate the constructors in BlocksController, TaxonomiesController, and MenuLinksControllerFile a follow-up ticket for the 3.0.x branch- #3519483: Remove constructors and extra constructor parameters from Controllers and Form classesRelease a 2.0.9 version- Deferred for #3519479: Deprecate the constructors in BlocksController, TaxonomiesController, and MenuLinksController
User interface changes
None.
API changes
None.
Data model changes
None.
Issue fork structure_sync-3518008
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
Comment #2
mparker17Reviews welcome!
Comment #4
spidermanI've reviewed this issue in some detail, looking carefully at the code changes, and also running tests locally on my branch. This was partly an exercise to re-familiarize myself with this project codebase, but also validated that the MR @mparker17 put together is solid.
Out of curiosity, I went looking for documentation about the "new style" dependency injection technique we're moving toward here. It took a bit of digging, but I believe what we're talking about is a move from "constructor injection" to "setter injection": https://symfony.com/doc/current/service_container/calls.html.
The reasoning for this makes sense to me, and I believe the
create()pattern ensures the downside of this approach (namely, that you can't guarantee your dependencies are present upon being constructed) are mitigated, although I couldn't find much to back this up (is there something in Symfony or Drupal's Kernel or some such that ensurescreate()is always called?)At any rate, this all looks great to me, and I'm marking as RTBC. I will file the follow-up issues now, and I think it's safe to commit this change anytime :)
Comment #5
mparker17I was about to merge this and noticed a new phpcs message...
... so I fixed it in another commit (changed "structure_sync:2.x" to "structure_sync:2.0.9", i.e.: the next version); and verified it made the message go away, and there were no new errors, so I'm going to merge this.
Comment #6
mparker17Crediting and updating issue summary
Comment #8
dydave commentedGreat job @mparker17 and @spiderman! 🥳
Thanks a lot for the great documentation in this ticket and the very clean approach taken in the merge request to deprecate previous API signatures. It's definitely a great example and source of inspiration to me in terms of steps you have taken to drop support for certain versions 👍
Glad to see the project moving forward!
Thanks again for all the great work to keep the module well maintained! 🙏
Comment #9
mparker17Thanks @dydave for the kind words! :)
Thanks @spiderman for filing a follow-up, #3519479: Deprecate the constructors in BlocksController, TaxonomiesController, and MenuLinksController
Comment #10
spidermanThanks @mparker17! I've also filed the other follow-up you mentioned, #3519483: Remove constructors and extra constructor parameters from Controllers and Form classes and ticked that off here.
I'm wondering if it makes sense to get #3519479: Deprecate the constructors in BlocksController, TaxonomiesController, and MenuLinksController in before cutting a new release, or if you prefer to cut 2.0.9 here, and then the further deprecations in a 2.0.10 release?
Comment #11
mparker17@spiderman, thank you very much!
I think it's worth it to get #3519479 in before cutting a new release.
Comment #12
spidermanOk great, in that case, I'll strike that task from the list and put it on #3519479: Deprecate the constructors in BlocksController, TaxonomiesController, and MenuLinksController instead.