diff --git a/core/.phpstan-baseline.php b/core/.phpstan-baseline.php index 6fe1bda75ca4d5790987bb7d2cb824cdb39a7b4d..0327dbcec473cc5cacad5306566650a5ac156343 100644 --- a/core/.phpstan-baseline.php +++ b/core/.phpstan-baseline.php @@ -56410,5 +56410,11 @@ 'count' => 1, 'path' => __DIR__ . '/themes/olivero/src/OliveroPreRender.php', ]; +$ignoreErrors[] = [ + 'message' => '#^Variable \\$render_start might not be defined\\.$#', + 'identifier' => 'variable.undefined', + 'count' => 1, + 'path' => __DIR__ . '/lib/Drupal/Core/Render/Renderer.php', +]; return ['parameters' => ['ignoreErrors' => $ignoreErrors]]; diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index c295fa5cb1b9f48f033c484d0d503b06de7a5381..0c6e276e3bf6a10b0efacf574145b9f7f794f3f9 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -321,11 +321,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) { '#create_placeholder' => TRUE, ]); - // If the default values for this element have not been loaded yet, populate - // them. - if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) { - $elements += $this->elementInfo->getInfo($elements['#type']); - } + $this->loadElementDefaults($elements); // First validate the usage of #lazy_builder; both of the next if-statements // use it if available. @@ -375,6 +371,9 @@ protected function doRender(&$elements, $is_root_call = FALSE) { // provide helpful details for troubleshooting. assert(is_array($new_elements), "#lazy_builder callbacks must return a valid renderable array, got " . gettype($new_elements) . " from " . Variable::callableToString($elements['#lazy_builder'][0])); + // The lazy builder could have set a #type, load its defaults. + $this->loadElementDefaults($new_elements); + // Retain the original cacheability metadata, plus cache keys. CacheableMetadata::createFromRenderArray($elements) ->merge(CacheableMetadata::createFromRenderArray($new_elements)) @@ -606,6 +605,18 @@ public function executeInRenderContext(RenderContext $context, callable $callabl return $result; } + /** + * Loads an element's default values based on its type. + * + * @param array $element + * The render array representing the element. + */ + protected function loadElementDefaults(array &$element): void { + if (isset($element['#type']) && empty($element['#defaults_loaded'])) { + $element += $this->elementInfo->getInfo($element['#type']); + } + } + /** * Returns the current render context. * diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php index f6eb9b74598042496a207bf01994184d16af5074..69301eae9bb8c954a601dcd5ad5839f5a524e2b8 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php @@ -552,6 +552,40 @@ public function testRenderSortingWithSetHashSorted(): void { $this->assertLessThan(strpos($output, $first), strpos($output, $second)); } + /** + * Tests that element defaults are added. + * + * @covers ::render + * @covers ::doRender + */ + public function testElementDefaultsAdded(): void { + $build = ['#type' => 'details']; + $this->renderer->renderInIsolation($build); + $this->assertTrue($build['#defaults_loaded'], "An element with a type had said type's defaults loaded."); + + $build = [ + '#lazy_builder' => [ + 'Drupal\Tests\Core\Render\TestCallables::lazyBuilder', + [FALSE], + ], + '#create_placeholder' => FALSE, + ]; + + $this->renderer->renderInIsolation($build); + $this->assertArrayNotHasKey('#defaults_loaded', $build, "A lazy builder that did not set a type had no type defaults loaded."); + + $build = [ + '#lazy_builder' => [ + 'Drupal\Tests\Core\Render\TestCallables::lazyBuilder', + [TRUE], + ], + '#create_placeholder' => FALSE, + ]; + + $this->renderer->renderInIsolation($build); + $this->assertTrue($build['#defaults_loaded'], "A lazy builder that set a type had said type's defaults loaded."); + } + /** * @covers ::render * @covers ::doRender @@ -1134,11 +1168,19 @@ public function preRenderPrinted($elements) { return $elements; } + public static function lazyBuilder(bool $set_type): array { + $build['content'] = ['#markup' => 'Content']; + if ($set_type) { + $build['#type'] = 'details'; + } + return $build; + } + /** * {@inheritdoc} */ public static function trustedCallbacks() { - return ['preRenderPrinted']; + return ['preRenderPrinted', 'lazyBuilder']; } }