Mercurial > hg > isophonics-drupal-site
diff core/lib/Drupal/Core/Render/BubbleableMetadata.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/lib/Drupal/Core/Render/BubbleableMetadata.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,177 @@ +<?php + +namespace Drupal\Core\Render; + +use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Cache\CacheableMetadata; + +/** + * Value object used for bubbleable rendering metadata. + * + * @see \Drupal\Core\Render\RendererInterface::render() + */ +class BubbleableMetadata extends CacheableMetadata implements AttachmentsInterface { + + use AttachmentsTrait; + + /** + * Merges the values of another bubbleable metadata object with this one. + * + * @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; + } + +}