Problem/Motivation
BlockPluginInterface::build() is documented as always returning an array.
When implementations do not return an array, the resulting bugs range from subtle errors to fatal whitescreens
Proposed resolution
Add return types to core block ::build() methods, add a deprecation warning to ::build() methods discovered without a return type, enforce the return type in Drupal 10.
Remaining tasks
Postponed on #3050720: [Meta] Implement strict typing in existing code
User interface changes
N/A
API changes
BlockPluginInterface::build() will require an array return type from Drupal 10.
Data model changes
N/A
Release notes snippet
Custom block plugins should now add the "array" return type to the ::build method.
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | 3164389-64.patch | 42.11 KB | nwom |
Issue fork drupal-3164389
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
longwaveInspired by BreadcrumbManager can we just do something like this? There is a null check further down, but should we even allow NULL returns?
Comment #3
tim.plunkettThe above patch is for block.module (though as you point out, it still allows for NULL defeating the purpose), and this issue was spun out of #3162699: Improve debugability of block plugins returning NULL in Layout Builder which will help Layout Builder. But this doesn't help Page Manager or any other contrib module using blocks.
Idk how best to fix it globally.
Comment #4
longwaveWe can deprecate ::build() without a return type and add it to the interface in Drupal 10.
This means we have to add return types across all of core, but not sure what to do about the possibility of contrib/custom code extending core blocks and overriding ::build()?
Comment #5
tim.plunkett$definition is not always an array, fixed that. As well as the existing classes.
This needs a deprecation test still.
Plugin implementations are not part of the public API; other code should not be extending from them.
https://www.drupal.org/core/d8-bc-policy#plugins
Comment #7
longwaveApart from the unit tests is there a valid case where $class can still be NULL by the time we get here?
Comment #8
tim.plunkett#7 no, but I copied the if/else from the parent ::processDefinition().
Maybe
\Drupal\Component\Plugin\Factory\DefaultFactory::getPluginClass()should be used instead? Hmm.Working on fixing test failues
Comment #10
tim.plunkettWrote a draft CR, added test coverage, fixed bugs this surfaced.
https://www.drupal.org/node/3164649
Comment #11
longwaveI did a double take at this as I thought
$expectedshould containblock1, then I realised it's because of the dummy provider, is this worth changing?Otherwise the patch and the CR both look good, unsure if I can RTBC though.
Comment #12
longwaveUpdated IS.
Comment #13
eclipsegc commentedI love this. Typed returns++
Any reason this shouldn't be RTBC?
Comment #14
tim.plunkettAddressing #11 via docs, as well as restoring a couple lines of docs I incorrectly moved.
Leaving at RTBC.
Comment #15
longwaveNit: thagt -> that
(spellchecker in core won't let this be committed now!)
Comment #16
tim.plunkettWhy doesn't that fail patches? 🤔
Comment #17
longwave#16: #3122096: Support code spell checking
Comment #18
eclipsegc commentedJust ++ing the rtbc. No functional changes.
Comment #19
jungleUnused use statement removed. Stay RTBC.
Comment #20
tim.plunkettThanks for the link in #17!
Comment #21
catchThere are no changes to BlockPluginInterface here. Even if we can't add the return type hint in 9.1.x, we should document there that it will be added in 10.0.x I think?
Comment #22
tim.plunkettYep, good call. Added an explicit @todo with a follow-up for D10: #3167432: Add array return type to ::build() on BlockPluginInterface
Comment #23
eclipsegc commentedThink we're back to RTBC.
Comment #24
alexpottSo this will break any contrib or custom code that extends block and overrides a build method - see https://3v4l.org/rbmaP
I have argued in the past that we should mark all blocks as final - see #3019332: Use final to define classes that are NOT extension points - unfortunately this has proved contentious but if we don't do that making changing like this is always going to be hard. Given this is the first issue in runtime code about return typehints (we have PHPUnit requiring them in the test system) I think we need release manager input on the way forward. I think we should mark all core blocks as final because then adding return typehints to other part of of the interface in a similar fashion will be simpler and there is then only one "break".
Comment #25
tim.plunkettGrepping contrib, there are 5 modules that extend core blocks. All as new plugins, none as a plugin alter.
SystemBrandingBlock:
- amp
SystemMenuBlock:
- domain_menu_access
- domain_menus
- drulma_companion
- menu_item_fields
Looking at SystemMenuBlock, there is a LOT of useful stuff in there and I don't blame them for overriding it. If that were to be marked final, it'd be considerate of us to provide a service or a trait to encapsulate that code.
Comment #26
tim.plunkettThis doesn't address the problems of SystemMenuBlock (or SystemBrandingBlock), but here are all the blocks marked final.
Comment #28
tim.plunkettThere's a workaround in \Drupal\layout_builder_fieldblock_test\Plugin\Block\FieldBlock that will no longer work.
And then there's
Not pushing on these until further discussion happens around
finalComment #29
longwaveAlternatively do we need to warn contrib with another deprecation?
Comment #30
longwaveComment #31
tim.plunkettMy initial thoughts are that at least the typehint break is a one-line automatic change, and could be paired with a wakeup call change record about not extending blocks.
Making all of the core blocks
finalis going to make things rough to work around.I wrote the above before #29, and now I'm even more convinced that #29 is a good step.
Except that we have abstract block classes which are totally okay to extend from. My reflection is a bit rusty, can you instead check that the parent is abstract?
Comment #32
catchYes this is my feeling as well. If you extend a block, you might or might not be OK, and you might or might not need to make a one line change. If we made the blocks final in a minor, then every extending class immediately gets a hard break - it's something we should only do in a major release IMO (apart from the further argument about whether to do it or not in general).
Also adding the return type hint works fine in the other direction - so a module that updates for 9.1.x will be compatible with 9.0.x without having to branch or raise minimum requirements etc.
Comment #34
longwave> Except that we have abstract block classes which are totally okay to extend from. My reflection is a bit rusty, can you instead check that the parent is abstract?
Yep, we can, let's see how this fares.
Comment #35
longwaveI even wonder if we should move this up a layer to DefaultPluginManager - ie. we should extend this to all plugins, not just blocks.
Comment #37
longwaveFixing test failures.
Comment #38
joseph.olstadWow, one very large patch!
FWIW: I just did a dry run on 9.0.x, the patch applies cleanly so I triggered tests on 9.0.x alsolooks like it doesn't apply to 9.0.x after all, for some reason.Comment #39
eclipsegc commentedOk, so a few points here:
Thoughts?
Comment #41
tim.plunkettRerolled. Still needs to address #39
Comment #43
anmolgoyal74 commentedUpdated deprecated @expectedDeprecation annotation.
Comment #44
nwom commentedI couldn't get any of the previous patches to apply on D9.07:
error: core/modules/node/tests/modules/node_block_test/src/Plugin/Block/NodeContextTestBlock.php: No such file or directoryFor those looking for a quick solution the following patch works: #3121395: Fatal error after add Main page content block layout builder
Comment #45
chandan.drupalchamp commentedRerolled patch for 9.0.
Comment #46
nwom commented#45 fixes the problem where the Main Content block cannot be added to the Layout Builder variant for Panels Everywhere, as discussed here: #3143487: Not compatible with "Layout Builder" variant as site template (Workaround found).
However, as mentioned in #4, it does indeed cause problems with certain contrib modules. Superfish has the following error message after applying the patch (See: https://www.drupal.org/project/superfish/issues/2951400#comment-13919952):
PHP Fatal error: Declaration of Drupal\superfish\Plugin\Block\SuperfishBlock::build() must be compatible with Drupal\system\Plugin\Block\SystemMenuBlock::build(): array in /modules/contrib/superfish/src/Plugin/Block/SuperfishBlock.php on line 24Comment #47
andypostNeeds to update patch deprecation for 9.2.0
Comment #48
Pooja Ganjage commentedHi,
Creating a patch as suggested in #47 comment.
Please review the patch.
Thanks.
Comment #49
Pooja Ganjage commentedComment #50
Pooja Ganjage commentedComment #52
lobodakyrylo commented#50 patch is not applied to 9.2.0 version. I've created a new one.
Comment #53
tim.plunkettRerolled for 9.3.x and switched to a merge request instead of patches.
Comment #55
tim.plunkettRandom fail: #3208791: [random test failure] Random fail in LayoutBuilderDisableInteractionsTest
Comment #56
phenaproximaThis seems straightforward to me.
Comment #57
phenaproximaAh, sorry -- I found one more thing that might be a condition to defend against.
Comment #58
xjmI'm definitely concerned about the part where we add typehints to existing core classes. Plugin implementations are one of those technically internal, but nonetheless tempting to subclass, internal APIs. I also agree with:
@Berdir expresses related perspective in #3019332-3: Use final to define classes that are NOT extension points.
Furthermore, we have an expectation of providing deprecations rather than BC breaks even for internal APIs. Adding a return typehint to a parent class definitely qualifies. There may only be five usages in contrib, but there could be many more in custom code, and plugins are the kind of thing that's likely to get reused and tweaked there.
Both release managers have already indicated they are not presently comfortable adding
finalto things like plugins and controllers (in the referenced issue).I think we should split this issue up, which @tim.plunkett also suggested in chat.
Comment #59
xjmComment #61
sseto commentedHi! Is there a patch for 9.3?
Comment #64
nwom commentedThe patch no longer applied to 9.4.8. It was only failing on
core/tests/Drupal/Tests/Core/Block/BlockManagerTest.php.Here is a temporary patch for those that need it without the test. I'm sure it would be better updated as a merge request anyways.
Comment #65
catchPostponing this on #3050720: [Meta] Implement strict typing in existing code where (or in a sub-issue) we'll add deprecation support for adding return type hints.
Comment #67
quietone commentedAdding postponed item to the list of remaining tasks per the issue summary field documentation, remaining task.