diff --git a/core/lib/Drupal/Core/Block/BlockManager.php b/core/lib/Drupal/Core/Block/BlockManager.php index 8f5cd7515d..8230b24142 100644 --- a/core/lib/Drupal/Core/Block/BlockManager.php +++ b/core/lib/Drupal/Core/Block/BlockManager.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Block; use Drupal\Component\Plugin\Definition\PluginDefinitionInterface; +use Drupal\Component\Plugin\Factory\DefaultFactory; use Drupal\Component\Plugin\FallbackPluginManagerInterface; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; @@ -67,18 +68,10 @@ public function processDefinition(&$definition, $plugin_id) { parent::processDefinition($definition, $plugin_id); $this->processDefinitionCategory($definition); - $class = NULL; - if ($definition instanceof PluginDefinitionInterface) { - $class = $definition->getClass(); - } - elseif (is_array($definition) && isset($definition['class'])) { - $class = $definition['class']; - } - if ($class) { - $build_method = new \ReflectionMethod($class, 'build'); - if (!$build_method->hasReturnType() || $build_method->getReturnType()->getName() !== 'array') { - @trigger_error('Declaring ::build() without an array return typehint in ' . $class . ' is deprecated in drupal:9.1.0. Typehinting will be required before drupal:10.0.0. See https://www.drupal.org/node/xxxxxxx', E_USER_DEPRECATED); - } + $class = DefaultFactory::getPluginClass($plugin_id, $definition); + $build_method = new \ReflectionMethod($class, 'build'); + if (!$build_method->hasReturnType() || $build_method->getReturnType()->getName() !== 'array') { + @trigger_error('Declaring ::build() without an array return typehint in ' . $class . ' is deprecated in drupal:9.1.0. Typehinting will be required before drupal:10.0.0. See https://www.drupal.org/node/3164649.', E_USER_DEPRECATED); } } diff --git a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php index ddf59bc53c..3bea4f3929 100644 --- a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php +++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php @@ -126,6 +126,7 @@ public function blockSubmit($form, FormStateInterface $form_state) { * {@inheritdoc} */ public function build(): array { + $build = []; // Load the selected feed. if ($feed = $this->feedStorage->load($this->configuration['feed'])) { $result = $this->itemStorage->getQuery() @@ -155,9 +156,9 @@ public function build(): array { '#url' => $feed->toUrl(), '#attributes' => ['title' => $this->t("View this feed's recent news.")], ]; - return $build; } } + return $build; } /** diff --git a/core/modules/system/src/Plugin/Block/SystemMainBlock.php b/core/modules/system/src/Plugin/Block/SystemMainBlock.php index 402474143c..869b21b3bf 100644 --- a/core/modules/system/src/Plugin/Block/SystemMainBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemMainBlock.php @@ -4,6 +4,7 @@ use Drupal\Core\Block\BlockBase; use Drupal\Core\Block\MainContentBlockPluginInterface; +use Drupal\Core\Logger\LoggerChannelTrait; /** * Provides a 'Main page content' block. @@ -18,6 +19,8 @@ */ class SystemMainBlock extends BlockBase implements MainContentBlockPluginInterface { + use LoggerChannelTrait; + /** * The render array representing the main page content. * @@ -36,6 +39,10 @@ public function setMainContent(array $main_content) { * {@inheritdoc} */ public function build(): array { + if (!is_array($this->mainContent)) { + $this->getLogger('system')->error('The system_main_block was placed but ::setMainContent() was not called.'); + return []; + } return $this->mainContent; } diff --git a/core/tests/Drupal/Tests/Core/Block/BlockManagerTest.php b/core/tests/Drupal/Tests/Core/Block/BlockManagerTest.php index 824cc19900..87728c64ca 100644 --- a/core/tests/Drupal/Tests/Core/Block/BlockManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Block/BlockManagerTest.php @@ -4,9 +4,13 @@ use Drupal\Component\Plugin\Discovery\DiscoveryInterface; use Drupal\Core\Block\BlockManager; +use Drupal\Core\Block\BlockPluginInterface; +use Drupal\Core\Block\BlockPluginTrait; use Drupal\Core\Block\Plugin\Block\Broken; +use Drupal\Core\Cache\CacheableDependencyTrait; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Plugin\PluginBase; use Drupal\Tests\UnitTestCase; use Psr\Log\LoggerInterface; @@ -42,11 +46,7 @@ protected function setUp(): void { $this->logger = $this->prophesize(LoggerInterface::class); $this->blockManager = new BlockManager(new \ArrayObject(), $cache_backend->reveal(), $module_handler->reveal(), $this->logger->reveal()); $this->blockManager->setStringTranslation($this->getStringTranslationStub()); - - $discovery = $this->prophesize(DiscoveryInterface::class); - // Specify the 'broken' block, as well as 3 other blocks with admin labels - // that are purposefully not in alphabetical order. - $discovery->getDefinitions()->willReturn([ + $this->setDefinitions([ 'broken' => [ 'admin_label' => 'Broken/Missing', 'category' => 'Block', @@ -56,16 +56,32 @@ protected function setUp(): void { 'block1' => [ 'admin_label' => 'Coconut', 'category' => 'Group 2', + 'class' => TestBlockManagerBlock::class, ], 'block2' => [ 'admin_label' => 'Apple', 'category' => 'Group 1', + 'class' => TestBlockManagerBlock::class, ], 'block3' => [ 'admin_label' => 'Banana', 'category' => 'Group 2', + 'class' => TestBlockManagerBlock::class, ], ]); + } + + /** + * Sets the definitions the block manager will return. + * + * @param array $definitions + * An array of plugin definitions. + */ + protected function setDefinitions(array $definitions) { + $discovery = $this->prophesize(DiscoveryInterface::class); + // Specify the 'broken' block, as well as 3 other blocks with admin labels + // that are purposefully not in alphabetical order. + $discovery->getDefinitions()->willReturn($definitions); // Force the discovery object onto the block manager. $property = new \ReflectionProperty(BlockManager::class, 'discovery'); $property->setAccessible(TRUE); @@ -107,4 +123,47 @@ public function testHandlePluginNotFound() { $this->assertSame('broken', $plugin->getPluginId()); } + /** + * @group legacy + * @expectedDeprecation Declaring ::build() without an array return typehint in Drupal\Tests\Core\Block\TestBlockManagerNoArrayReturnTypeBlock is deprecated in drupal:9.1.0. Typehinting will be required before drupal:10.0.0. See https://www.drupal.org/node/3164649. + */ + public function testBuildNoReturnType() { + $this->setDefinitions([ + 'block1' => [ + 'provider' => 'test', + 'class' => TestBlockManagerNoArrayReturnTypeBlock::class, + ], + ]); + $expected = []; + $definitions = $this->blockManager->getDefinitions(); + $this->assertSame($expected, $definitions); + } + +} + +class TestBlockManagerBlock extends PluginBase implements BlockPluginInterface { + + use BlockPluginTrait; + use CacheableDependencyTrait; + + /** + * {@inheritdoc} + */ + public function build(): array { + return []; + } + +} + +class TestBlockManagerNoArrayReturnTypeBlock extends PluginBase implements BlockPluginInterface { + + use BlockPluginTrait; + use CacheableDependencyTrait; + + /** + * {@inheritdoc} + */ + public function build() { + } + }