Unverified Commit eebe8645 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott, mradcliffe:...

Issue #3143096 by jedihe, jyotimishra123, shetpooja04, alexpott, mradcliffe: [DX]: throw an exception if #lazy_builder callback does not return a (renderable) array

(cherry picked from commit e9a53cb2)
parent 7898708d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -353,6 +353,23 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
    // Build the element if it is still empty.
    if (isset($elements['#lazy_builder'])) {
      $new_elements = $this->doCallback('#lazy_builder', $elements['#lazy_builder'][0], $elements['#lazy_builder'][1]);
      // Throw an exception if #lazy_builder callback does not return an array;
      // provide helpful details for troubleshooting.
      if (!is_array($new_elements)) {
        $callable = $elements['#lazy_builder'][0];
        $callable_name = '[unknown]';
        if ($callable instanceof \Closure) {
          $callable_name = '[closure]';
        }
        elseif (is_array($callable)) {
          $callable_name = implode('::', $callable);
        }
        elseif (is_string($callable)) {
          $callable_name = $callable;
        }
        $wrong_type = gettype($new_elements);
        throw new \LogicException("#lazy_builder callbacks must return a valid renderable array, got $wrong_type from $callable_name");
      }
      // Retain the original cacheability metadata, plus cache keys.
      CacheableMetadata::createFromRenderArray($elements)
        ->merge(CacheableMetadata::createFromRenderArray($new_elements))
+48 −0
Original line number Diff line number Diff line
@@ -962,6 +962,54 @@ public function testCreatePlaceholderPropertyWithoutLazyBuilder() {
    $this->renderer->renderRoot($element);
  }

  /**
   * Tests lazy builders (string callable) that do not return a renderable.
   *
   * @covers ::render
   * @covers ::doRender
   */
  public function testNonArrayReturnFromLazyBuilderStringCallable() {
    $element = [];
    $element['#lazy_builder'] = ['Drupal\Tests\Core\Render\PlaceholdersTest::callbackNonArrayReturn', []];

    $this->expectException(\LogicException::class);
    $this->expectExceptionMessage('#lazy_builder callbacks must return a valid renderable array, got boolean from Drupal\Tests\Core\Render\PlaceholdersTest::callbackNonArrayReturn');
    $this->renderer->renderRoot($element);
  }

  /**
   * Tests lazy builders (array callable) that do not return a renderable.
   *
   * @covers ::render
   * @covers ::doRender
   */
  public function testNonArrayReturnFromLazyBuilderArrayCallable() {
    $element = [];
    $element['#lazy_builder'] = [['Drupal\Tests\Core\Render\PlaceholdersTest', 'callbackNonArrayReturn'], []];

    $this->expectException(\LogicException::class);
    $this->expectExceptionMessage('#lazy_builder callbacks must return a valid renderable array, got boolean from Drupal\Tests\Core\Render\PlaceholdersTest::callbackNonArrayReturn');
    $this->renderer->renderRoot($element);
  }

  /**
   * Tests lazy builders (closure) that do not return a renderable.
   *
   * @covers ::render
   * @covers ::doRender
   */
  public function testNonArrayReturnFromLazyBuilderClosure() {
    $element = [];
    $closure = function () {
      return NULL;
    };
    $element['#lazy_builder'] = [$closure, []];

    $this->expectException(\LogicException::class);
    $this->expectExceptionMessage('#lazy_builder callbacks must return a valid renderable array, got NULL from [closure]');
    $this->renderer->renderRoot($element);
  }

  /**
   * Create an element with a child and subchild. Each element has the same
   * #lazy_builder callback, but with different contexts. They don't modify
+11 −1
Original line number Diff line number Diff line
@@ -317,11 +317,21 @@ public static function callbackTagCurrentTemperature($animal) {
    return $build;
  }

  /**
   * Returns invalid renderable to #lazy_builder callback.
   *
   * @return bool
   *   TRUE.
   */
  public static function callbackNonArrayReturn() {
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public static function trustedCallbacks() {
    return ['callbackTagCurrentTemperature', 'callbackPerUser', 'callback'];
    return ['callbackTagCurrentTemperature', 'callbackPerUser', 'callback', 'callbackNonArrayReturn'];
  }

}