Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / Render / BubbleableMetadata.php
1 <?php
2
3 namespace Drupal\Core\Render;
4
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Cache\CacheableMetadata;
7
8 /**
9  * Value object used for bubbleable rendering metadata.
10  *
11  * @see \Drupal\Core\Render\RendererInterface::render()
12  */
13 class BubbleableMetadata extends CacheableMetadata implements AttachmentsInterface {
14
15   use AttachmentsTrait;
16
17   /**
18    * Merges the values of another bubbleable metadata object with this one.
19    *
20    * @param \Drupal\Core\Cache\CacheableMetadata $other
21    *   The other bubbleable metadata object.
22    *
23    * @return static
24    *   A new bubbleable metadata object, with the merged data.
25    */
26   public function merge(CacheableMetadata $other) {
27     $result = parent::merge($other);
28
29     // This is called many times per request, so avoid merging unless absolutely
30     // necessary.
31     if ($other instanceof BubbleableMetadata) {
32       if (empty($this->attachments)) {
33         $result->attachments = $other->attachments;
34       }
35       elseif (empty($other->attachments)) {
36         $result->attachments = $this->attachments;
37       }
38       else {
39         $result->attachments = static::mergeAttachments($this->attachments, $other->attachments);
40       }
41     }
42
43     return $result;
44   }
45
46   /**
47    * Applies the values of this bubbleable metadata object to a render array.
48    *
49    * @param array &$build
50    *   A render array.
51    */
52   public function applyTo(array &$build) {
53     parent::applyTo($build);
54     $build['#attached'] = $this->attachments;
55   }
56
57   /**
58    * Creates a bubbleable metadata object with values taken from a render array.
59    *
60    * @param array $build
61    *   A render array.
62    *
63    * @return static
64    */
65   public static function createFromRenderArray(array $build) {
66     $meta = parent::createFromRenderArray($build);
67     $meta->attachments = (isset($build['#attached'])) ? $build['#attached'] : [];
68     return $meta;
69   }
70
71   /**
72    * Creates a bubbleable metadata object from a depended object.
73    *
74    * @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
75    *   The object whose cacheability metadata to retrieve. If it implements
76    *   CacheableDependencyInterface, its cacheability metadata will be used,
77    *   otherwise, the passed in object must be assumed to be uncacheable, so
78    *   max-age 0 is set.
79    *
80    * @return static
81    */
82   public static function createFromObject($object) {
83     $meta = parent::createFromObject($object);
84
85     if ($object instanceof AttachmentsInterface) {
86       $meta->attachments = $object->getAttachments();
87     }
88
89     return $meta;
90   }
91
92   /**
93    * {@inheritdoc}
94    */
95   public function addCacheableDependency($other_object) {
96     parent::addCacheableDependency($other_object);
97
98     if ($other_object instanceof AttachmentsInterface) {
99       $this->addAttachments($other_object->getAttachments());
100     }
101
102     return $this;
103   }
104
105   /**
106    * Merges two attachments arrays (which live under the '#attached' key).
107    *
108    * The values under the 'drupalSettings' key are merged in a special way, to
109    * match the behavior of:
110    *
111    * @code
112    *   jQuery.extend(true, {}, $settings_items[0], $settings_items[1], ...)
113    * @endcode
114    *
115    * This means integer indices are preserved just like string indices are,
116    * rather than re-indexed as is common in PHP array merging.
117    *
118    * Example:
119    * @code
120    * function module1_page_attachments(&$page) {
121    *   $page['a']['#attached']['drupalSettings']['foo'] = ['a', 'b', 'c'];
122    * }
123    * function module2_page_attachments(&$page) {
124    *   $page['#attached']['drupalSettings']['foo'] = ['d'];
125    * }
126    * // When the page is rendered after the above code, and the browser runs the
127    * // resulting <SCRIPT> tags, the value of drupalSettings.foo is
128    * // ['d', 'b', 'c'], not ['a', 'b', 'c', 'd'].
129    * @endcode
130    *
131    * By following jQuery.extend() merge logic rather than common PHP array merge
132    * logic, the following are ensured:
133    * - Attaching JavaScript settings is idempotent: attaching the same settings
134    *   twice does not change the output sent to the browser.
135    * - If pieces of the page are rendered in separate PHP requests and the
136    *   returned settings are merged by JavaScript, the resulting settings are
137    *   the same as if rendered in one PHP request and merged by PHP.
138    *
139    * @param array $a
140    *   An attachments array.
141    * @param array $b
142    *   Another attachments array.
143    *
144    * @return array
145    *   The merged attachments array.
146    */
147   public static function mergeAttachments(array $a, array $b) {
148     // If both #attached arrays contain drupalSettings, then merge them
149     // correctly; adding the same settings multiple times needs to behave
150     // idempotently.
151     if (!empty($a['drupalSettings']) && !empty($b['drupalSettings'])) {
152       $drupalSettings = NestedArray::mergeDeepArray([$a['drupalSettings'], $b['drupalSettings']], TRUE);
153       // No need for re-merging them.
154       unset($a['drupalSettings']);
155       unset($b['drupalSettings']);
156     }
157     // Optimize merging of placeholders: no need for deep merging.
158     if (!empty($a['placeholders']) && !empty($b['placeholders'])) {
159       $placeholders = $a['placeholders'] + $b['placeholders'];
160       // No need for re-merging them.
161       unset($a['placeholders']);
162       unset($b['placeholders']);
163     }
164     // Apply the normal merge.
165     $a = array_merge_recursive($a, $b);
166     if (isset($drupalSettings)) {
167       // Save the custom merge for the drupalSettings.
168       $a['drupalSettings'] = $drupalSettings;
169     }
170     if (isset($placeholders)) {
171       // Save the custom merge for the placeholders.
172       $a['placeholders'] = $placeholders;
173     }
174     return $a;
175   }
176
177 }