You are here

class BubbleableMetadata in Drupal 9

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Render/BubbleableMetadata.php \Drupal\Core\Render\BubbleableMetadata

Value object used for bubbleable rendering metadata.

Hierarchy

Expanded class hierarchy of BubbleableMetadata

See also

\Drupal\Core\Render\RendererInterface::render()

66 files declare their use of BubbleableMetadata
Actions.php in core/lib/Drupal/Core/Render/Element/Actions.php
AjaxResponse.php in core/lib/Drupal/Core/Ajax/AjaxResponse.php
AliasPathProcessor.php in core/modules/path_alias/src/PathProcessor/AliasPathProcessor.php
AliasPathProcessorTest.php in core/modules/path_alias/tests/src/Unit/PathProcessor/AliasPathProcessorTest.php
AttachedRenderingBlock.php in core/modules/system/tests/modules/render_attached_test/src/Plugin/Block/AttachedRenderingBlock.php

... See full list

File

core/lib/Drupal/Core/Render/BubbleableMetadata.php, line 13

Namespace

Drupal\Core\Render
View source
class BubbleableMetadata extends CacheableMetadata implements AttachmentsInterface {
  use AttachmentsTrait;

  /**
   * Creates a new bubbleable metadata object by merging this one with another.
   *
   * @param \Drupal\Core\Cache\CacheableMetadata $other
   *   The other bubbleable metadata object.
   *
   * @return static
   *   A new bubbleable metadata object, with the merged data.
   */
  public function merge(CacheableMetadata $other) {
    $result = parent::merge($other);

    // This is called many times per request, so avoid merging unless absolutely
    // necessary.
    if ($other instanceof BubbleableMetadata) {
      if (empty($this->attachments)) {
        $result->attachments = $other->attachments;
      }
      elseif (empty($other->attachments)) {
        $result->attachments = $this->attachments;
      }
      else {
        $result->attachments = static::mergeAttachments($this->attachments, $other->attachments);
      }
    }
    return $result;
  }

  /**
   * Applies the values of this bubbleable metadata object to a render array.
   *
   * @param array &$build
   *   A render array.
   */
  public function applyTo(array &$build) {
    parent::applyTo($build);
    $build['#attached'] = $this->attachments;
  }

  /**
   * Creates a bubbleable metadata object with values taken from a render array.
   *
   * @param array $build
   *   A render array.
   *
   * @return static
   */
  public static function createFromRenderArray(array $build) {
    $meta = parent::createFromRenderArray($build);
    $meta->attachments = isset($build['#attached']) ? $build['#attached'] : [];
    return $meta;
  }

  /**
   * Creates a bubbleable metadata object from a depended object.
   *
   * @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
   *   The object whose cacheability metadata to retrieve. If it implements
   *   CacheableDependencyInterface, its cacheability metadata will be used,
   *   otherwise, the passed in object must be assumed to be uncacheable, so
   *   max-age 0 is set.
   *
   * @return static
   */
  public static function createFromObject($object) {
    $meta = parent::createFromObject($object);
    if ($object instanceof AttachmentsInterface) {
      $meta->attachments = $object
        ->getAttachments();
    }
    return $meta;
  }

  /**
   * {@inheritdoc}
   */
  public function addCacheableDependency($other_object) {
    parent::addCacheableDependency($other_object);
    if ($other_object instanceof AttachmentsInterface) {
      $this
        ->addAttachments($other_object
        ->getAttachments());
    }
    return $this;
  }

  /**
   * Merges two attachments arrays (which live under the '#attached' key).
   *
   * The values under the 'drupalSettings' key are merged in a special way, to
   * match the behavior of:
   *
   * @code
   *   jQuery.extend(true, {}, $settings_items[0], $settings_items[1], ...)
   * @endcode
   *
   * This means integer indices are preserved just like string indices are,
   * rather than re-indexed as is common in PHP array merging.
   *
   * Example:
   * @code
   * function module1_page_attachments(&$page) {
   *   $page['a']['#attached']['drupalSettings']['foo'] = ['a', 'b', 'c'];
   * }
   * function module2_page_attachments(&$page) {
   *   $page['#attached']['drupalSettings']['foo'] = ['d'];
   * }
   * // When the page is rendered after the above code, and the browser runs the
   * // resulting <SCRIPT> tags, the value of drupalSettings.foo is
   * // ['d', 'b', 'c'], not ['a', 'b', 'c', 'd'].
   * @endcode
   *
   * By following jQuery.extend() merge logic rather than common PHP array merge
   * logic, the following are ensured:
   * - Attaching JavaScript settings is idempotent: attaching the same settings
   *   twice does not change the output sent to the browser.
   * - If pieces of the page are rendered in separate PHP requests and the
   *   returned settings are merged by JavaScript, the resulting settings are
   *   the same as if rendered in one PHP request and merged by PHP.
   *
   * @param array $a
   *   An attachments array.
   * @param array $b
   *   Another attachments array.
   *
   * @return array
   *   The merged attachments array.
   */
  public static function mergeAttachments(array $a, array $b) {

    // If both #attached arrays contain drupalSettings, then merge them
    // correctly; adding the same settings multiple times needs to behave
    // idempotently.
    if (!empty($a['drupalSettings']) && !empty($b['drupalSettings'])) {
      $drupalSettings = NestedArray::mergeDeepArray([
        $a['drupalSettings'],
        $b['drupalSettings'],
      ], TRUE);

      // No need for re-merging them.
      unset($a['drupalSettings']);
      unset($b['drupalSettings']);
    }

    // Optimize merging of placeholders: no need for deep merging.
    if (!empty($a['placeholders']) && !empty($b['placeholders'])) {
      $placeholders = $a['placeholders'] + $b['placeholders'];

      // No need for re-merging them.
      unset($a['placeholders']);
      unset($b['placeholders']);
    }

    // Apply the normal merge.
    $a = array_merge_recursive($a, $b);
    if (isset($drupalSettings)) {

      // Save the custom merge for the drupalSettings.
      $a['drupalSettings'] = $drupalSettings;
    }
    if (isset($placeholders)) {

      // Save the custom merge for the placeholders.
      $a['placeholders'] = $placeholders;
    }
    return $a;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
AttachmentsTrait::$attachments protected property The attachments for this response.
AttachmentsTrait::addAttachments public function
AttachmentsTrait::getAttachments public function
AttachmentsTrait::setAttachments public function
BubbleableMetadata::addCacheableDependency public function Overrides RefinableCacheableDependencyTrait::addCacheableDependency
BubbleableMetadata::applyTo public function Applies the values of this bubbleable metadata object to a render array. Overrides CacheableMetadata::applyTo
BubbleableMetadata::createFromObject public static function Creates a bubbleable metadata object from a depended object. Overrides CacheableMetadata::createFromObject
BubbleableMetadata::createFromRenderArray public static function Creates a bubbleable metadata object with values taken from a render array. Overrides CacheableMetadata::createFromRenderArray
BubbleableMetadata::merge public function Creates a new bubbleable metadata object by merging this one with another. Overrides CacheableMetadata::merge
BubbleableMetadata::mergeAttachments public static function Merges two attachments arrays (which live under the '#attached' key).
CacheableDependencyTrait::$cacheContexts protected property Cache contexts.
CacheableDependencyTrait::$cacheMaxAge protected property Cache max-age.
CacheableDependencyTrait::$cacheTags protected property Cache tags.
CacheableDependencyTrait::setCacheability protected function Sets cacheability; useful for value object constructors.
CacheableMetadata::getCacheContexts public function The cache contexts associated with this object. Overrides CacheableDependencyTrait::getCacheContexts
CacheableMetadata::getCacheMaxAge public function The maximum age for which this object may be cached. Overrides CacheableDependencyTrait::getCacheMaxAge
CacheableMetadata::getCacheTags public function The cache tags associated with this object. Overrides CacheableDependencyTrait::getCacheTags
CacheableMetadata::setCacheContexts public function Sets cache contexts.
CacheableMetadata::setCacheMaxAge public function Sets the maximum age (in seconds).
CacheableMetadata::setCacheTags public function Sets cache tags.
RefinableCacheableDependencyTrait::addCacheContexts public function
RefinableCacheableDependencyTrait::addCacheTags public function
RefinableCacheableDependencyTrait::mergeCacheMaxAge public function