Commit 4f37cf02 authored by catch's avatar catch

Issue #1700382 by Albert Volkman, cam8001: Replace remaining references to...

Issue #1700382 by Albert Volkman, cam8001: Replace remaining references to drupal_attributes() with new Attributes().
parent d62c798d
......@@ -1246,8 +1246,9 @@ function flood_is_allowed($name, $threshold, $window = 3600, $identifier = NULL)
* to being output to an HTML attribute value. It is often called as part of
* check_url() or filter_xss(), but those functions return an HTML-encoded
* string, so this function can be called independently when the output needs to
* be a plain-text string for passing to t(), l(), drupal_attributes(), or
* another function that will call check_plain() separately.
* be a plain-text string for passing to t(), l(),
* Drupal\Core\Template\Attribute, or another function that will call
* check_plain() separately.
*
* @param $uri
* A plain-text URI that might contain dangerous protocols.
......@@ -1300,10 +1301,11 @@ function drupal_strip_dangerous_protocols($uri) {
* @return
* A URI stripped of dangerous protocols and encoded for output to an HTML
* attribute value. Because it is already encoded, it should not be set as a
* value within a $attributes array passed to drupal_attributes(), because
* drupal_attributes() expects those values to be plain-text strings. To pass
* a filtered URI to drupal_attributes(), call
* drupal_strip_dangerous_protocols() instead.
* value within a $attributes array passed to Drupal\Core\Template\Attribute,
* because Drupal\Core\Template\Attribute expects those values to be
* plain-text strings. To pass a filtered URI to
* Drupal\Core\Template\Attribute, call drupal_strip_dangerous_protocols()
* instead.
*
* @see drupal_strip_dangerous_protocols()
*/
......@@ -1661,7 +1663,7 @@ function format_xml_elements($array) {
if ($value['key']) {
$output .= ' <' . $value['key'];
if (isset($value['attributes']) && is_array($value['attributes'])) {
$output .= drupal_attributes($value['attributes']);
$output .= new Attribute($value['attributes']);
}
if (isset($value['value']) && $value['value'] != '') {
......@@ -2242,46 +2244,6 @@ function drupal_http_header_attributes(array $attributes = array()) {
return $attributes ? ' ' . implode('; ', $attributes) : '';
}
/**
* Converts an associative array to an XML/HTML tag attribute string.
*
* Each array key and its value will be formatted into an attribute string.
* If a value is itself an array, then its elements are concatenated to a single
* space-delimited string (for example, a class attribute with multiple values).
*
* Attribute values are sanitized by running them through check_plain().
* Attribute names are not automatically sanitized. When using user-supplied
* attribute names, it is strongly recommended to allow only white-listed names,
* since certain attributes carry security risks and can be abused.
*
* Examples of security aspects when using drupal_attributes:
* @code
* // By running the value in the following statement through check_plain,
* // the malicious script is neutralized.
* drupal_attributes(array('title' => t('<script>steal_cookie();</script>')));
*
* // The statement below demonstrates dangerous use of drupal_attributes, and
* // will return an onmouseout attribute with JavaScript code that, when used
* // as attribute in a tag, will cause users to be redirected to another site.
* //
* // In this case, the 'onmouseout' attribute should not be whitelisted --
* // you don't want users to have the ability to add this attribute or others
* // that take JavaScript commands.
* drupal_attributes(array('onmouseout' => 'window.location="http://malicious.com/";')));
* @endcode
*
* @param $attributes
* An associative array of key-value pairs to be converted to attributes.
*
* @return
* A string ready for insertion in a tag (starts with a space).
*
* @ingroup sanitization
*/
function drupal_attributes(array $attributes = array()) {
return new Attribute($attributes);
}
/**
* Formats an internal or external URL link as an HTML anchor tag.
*
......@@ -2303,7 +2265,8 @@ function drupal_attributes(array $attributes = array()) {
* - 'attributes': An associative array of HTML attributes to apply to the
* anchor tag. If element 'class' is included, it must be an array; 'title'
* must be a string; other elements are more flexible, as they just need
* to work in a call to drupal_attributes($options['attributes']).
* to work as an argument for the constructor of the class
* Drupal\Core\Template\Attribute($options['attributes']).
* - 'html' (default FALSE): Whether $text is HTML or just plain-text. For
* example, to make an image tag into a link, this must be set to TRUE, or
* you will see the escaped HTML image tag. $text is not sanitized if
......@@ -2370,7 +2333,7 @@ function l($text, $path, array $options = array()) {
}
// The result of url() is a plain-text URL. Because we are using it here
// in an HTML argument context, we need to encode it properly.
return '<a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
return '<a href="' . check_plain(url($path, $options)) . '"' . new Attribute($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
}
/**
......
......@@ -6,6 +6,7 @@
*/
use Drupal\Core\Utility\Color;
use Drupal\Core\Template\Attribute;
/**
* @defgroup forms Form builder functions
......@@ -2670,7 +2671,7 @@ function theme_select($variables) {
element_set_attributes($element, array('id', 'name', 'size'));
_form_set_class($element, array('form-select'));
return '<select' . drupal_attributes($element['#attributes']) . '>' . form_select_options($element) . '</select>';
return '<select' . new Attribute($element['#attributes']) . '>' . form_select_options($element) . '</select>';
}
/**
......@@ -2784,7 +2785,7 @@ function theme_fieldset($variables) {
element_set_attributes($element, array('id'));
_form_set_class($element, array('form-wrapper'));
$output = '<fieldset' . drupal_attributes($element['#attributes']) . '>';
$output = '<fieldset' . new Attribute($element['#attributes']) . '>';
if (!empty($element['#title'])) {
// Always wrap fieldset legends in a SPAN for CSS positioning.
$output .= '<legend><span class="fieldset-legend">' . $element['#title'] . '</span></legend>';
......@@ -2806,7 +2807,8 @@ function theme_fieldset($variables) {
* Returns HTML for a radio button form element.
*
* Note: The input "name" attribute needs to be sanitized before output, which
* is currently done by passing all attributes to drupal_attributes().
* is currently done by initializing Drupal\Core\Template\Attribute with
* all the attributes.
*
* @param $variables
* An associative array containing:
......@@ -2826,7 +2828,7 @@ function theme_radio($variables) {
}
_form_set_class($element, array('form-radio'));
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -2853,7 +2855,7 @@ function theme_radios($variables) {
if (isset($element['#attributes']['title'])) {
$attributes['title'] = $element['#attributes']['title'];
}
return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
return '<div' . new Attribute($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
}
/**
......@@ -2932,7 +2934,7 @@ function theme_date($variables) {
}
$attributes['class'][] = 'container-inline';
return '<div' . drupal_attributes($attributes) . '>' . drupal_render_children($element) . '</div>';
return '<div' . new Attribute($attributes) . '>' . drupal_render_children($element) . '</div>';
}
/**
......@@ -3054,8 +3056,8 @@ function form_process_radios($element) {
$element[$key] += array(
'#type' => 'radio',
'#title' => $choice,
// The key is sanitized in drupal_attributes() during output from the
// theme function.
// The key is sanitized in Drupal\Core\Template\Attribute during output
// from the theme function.
'#return_value' => $key,
// Use default or FALSE. A value of FALSE means that the radio button is
// not 'checked'.
......@@ -3094,7 +3096,7 @@ function theme_checkbox($variables) {
}
_form_set_class($element, array('form-checkbox'));
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -3120,7 +3122,7 @@ function theme_checkboxes($variables) {
if (isset($element['#attributes']['title'])) {
$attributes['title'] = $element['#attributes']['title'];
}
return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
return '<div' . new Attribute($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
}
/**
......@@ -3338,7 +3340,7 @@ function theme_container($variables) {
$element['#attributes']['class'][] = 'form-wrapper';
}
return '<div' . drupal_attributes($element['#attributes']) . '>' . $element['#children'] . '</div>';
return '<div' . new Attribute($element['#attributes']) . '>' . $element['#children'] . '</div>';
}
/**
......@@ -3914,7 +3916,7 @@ function theme_button($variables) {
$element['#attributes']['class'][] = 'form-button-disabled';
}
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -3943,7 +3945,7 @@ function theme_image_button($variables) {
$element['#attributes']['class'][] = 'form-button-disabled';
}
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -3960,7 +3962,7 @@ function theme_hidden($variables) {
$element = $variables['element'];
$element['#attributes']['type'] = 'hidden';
element_set_attributes($element, array('name', 'value'));
return '<input' . drupal_attributes($element['#attributes']) . " />\n";
return '<input' . new Attribute($element['#attributes']) . " />\n";
}
/**
......@@ -3980,7 +3982,7 @@ function theme_textfield($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-text'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4000,7 +4002,7 @@ function theme_email($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-email'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4034,7 +4036,7 @@ function theme_tel($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-tel'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4055,7 +4057,7 @@ function theme_number($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max', 'placeholder'));
_form_set_class($element, array('form-number'));
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
$output = '<input' . new Attribute($element['#attributes']) . ' />';
return $output;
}
......@@ -4078,7 +4080,7 @@ function theme_range($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max'));
_form_set_class($element, array('form-range'));
$output = '<input' . drupal_attributes($element['#attributes']) . ' />';
$output = '<input' . new Attribute($element['#attributes']) . ' />';
return $output;
}
......@@ -4170,7 +4172,7 @@ function theme_url($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-url'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4190,7 +4192,7 @@ function theme_search($variables) {
element_set_attributes($element, array('id', 'name', 'value', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-search'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4245,7 +4247,7 @@ function theme_color($variables) {
element_set_attributes($element, array('id', 'name', 'value'));
_form_set_class($element, array('form-color'));
return '<input' . drupal_attributes($element['#attributes']) . ' />' . drupal_render_children($element);
return '<input' . new Attribute($element['#attributes']) . ' />' . drupal_render_children($element);
}
/**
......@@ -4268,7 +4270,7 @@ function theme_form($variables) {
$element['#attributes']['accept-charset'] = "UTF-8";
}
// Anonymous DIV to satisfy XHTML compliance.
return '<form' . drupal_attributes($element['#attributes']) . '><div>' . $element['#children'] . '</div></form>';
return '<form' . new Attribute($element['#attributes']) . '><div>' . $element['#children'] . '</div></form>';
}
/**
......@@ -4296,8 +4298,8 @@ function theme_textarea($variables) {
$element['#attributes']['class'][] = 'resize-' . $element['#resizable'];
}
$output = '<div' . drupal_attributes($wrapper_attributes) . '>';
$output .= '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
$output = '<div' . new Attribute($wrapper_attributes) . '>';
$output .= '<textarea' . new Attribute($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
$output .= '</div>';
return $output;
}
......@@ -4319,7 +4321,7 @@ function theme_password($variables) {
element_set_attributes($element, array('id', 'name', 'size', 'maxlength', 'placeholder'));
_form_set_class($element, array('form-text'));
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -4369,7 +4371,7 @@ function theme_file($variables) {
element_set_attributes($element, array('id', 'name', 'size'));
_form_set_class($element, array('form-file'));
return '<input' . drupal_attributes($element['#attributes']) . ' />';
return '<input' . new Attribute($element['#attributes']) . ' />';
}
/**
......@@ -4445,7 +4447,7 @@ function theme_form_element($variables) {
if (!empty($element['#attributes']['disabled'])) {
$attributes['class'][] = 'form-disabled';
}
$output = '<div' . drupal_attributes($attributes) . '>' . "\n";
$output = '<div' . new Attribute($attributes) . '>' . "\n";
// If #title is not set, we don't display any label or required marker.
if (!isset($element['#title'])) {
......@@ -4478,7 +4480,7 @@ function theme_form_element($variables) {
if (!empty($element['#id'])) {
$attributes['id'] = $element['#id'] . '--description';
}
$output .= '<div' . drupal_attributes($attributes) . '>' . $element['#description'] . "</div>\n";
$output .= '<div' . new Attribute($attributes) . '>' . $element['#description'] . "</div>\n";
}
$output .= "</div>\n";
......@@ -4502,7 +4504,7 @@ function theme_form_required_marker($variables) {
'class' => 'form-required',
'title' => $t('This field is required.'),
);
return '<abbr' . drupal_attributes($attributes) . '>*</abbr>';
return '<abbr' . new Attribute($attributes) . '>*</abbr>';
}
/**
......@@ -4558,7 +4560,7 @@ function theme_form_element_label($variables) {
}
// The leading whitespace helps visually separate fields from inline labels.
return ' <label' . drupal_attributes($attributes) . '>' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "</label>\n";
return ' <label' . new Attribute($attributes) . '>' . $t('!title !required', array('!title' => $title, '!required' => $required)) . "</label>\n";
}
/**
......
......@@ -5,6 +5,8 @@
* API for the Drupal menu system.
*/
use Drupal\Core\Template\Attribute;
/**
* @defgroup menu Menu system
* @{
......@@ -1602,7 +1604,7 @@ function theme_menu_link(array $variables) {
$sub_menu = drupal_render($element['#below']);
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
return '<li' . new Attribute($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}
/**
......
......@@ -5,6 +5,8 @@
* Functions to aid in presenting database results as a set of pages.
*/
use Drupal\Core\Template\Attribute;
/**
* Returns the current page being requested for display within a pager.
*
......@@ -476,7 +478,7 @@ function theme_pager_link($variables) {
// possible to use l() here.
// @see http://drupal.org/node/1410574
$attributes['href'] = url(current_path(), array('query' => $query));
return '<a' . drupal_attributes($attributes) . '>' . check_plain($text) . '</a>';
return '<a' . new Attribute($attributes) . '>' . check_plain($text) . '</a>';
}
/**
......
......@@ -9,6 +9,7 @@
*/
use Drupal\Core\Utility\ThemeRegistry;
use Drupal\Core\Template\Attribute;
/**
* @defgroup content_flags Content markers
......@@ -1503,7 +1504,7 @@ function template_preprocess_datetime(&$variables) {
* @see http://www.w3.org/TR/html5-author/the-time-element.html#attr-time-datetime
*/
function theme_datetime($variables) {
$output = '<time' . drupal_attributes($variables['attributes']) . '>';
$output = '<time' . new Attribute($variables['attributes']) . '>';
$output .= !empty($variables['html']) ? $variables['text'] : check_plain($variables['text']);
$output .= '</time>';
return $output;
......@@ -1569,7 +1570,7 @@ function theme_status_messages($variables) {
* @see l()
*/
function theme_link($variables) {
return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . new Attribute($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
}
/**
......@@ -1632,12 +1633,12 @@ function theme_links($variables) {
$heading['attributes']['class'] = $heading['class'];
}
$output .= '<' . $heading['level'] . drupal_attributes($heading['attributes']) . '>';
$output .= '<' . $heading['level'] . new Attribute($heading['attributes']) . '>';
$output .= check_plain($heading['text']);
$output .= '</' . $heading['level'] . '>';
}
$output .= '<ul' . drupal_attributes($attributes) . '>';
$output .= '<ul' . new Attribute($attributes) . '>';
$num_links = count($links);
$i = 0;
......@@ -1673,12 +1674,12 @@ function theme_links($variables) {
'html' => FALSE,
'attributes' => array(),
);
$item = '<span' . drupal_attributes($link['attributes']) . '>';
$item = '<span' . new Attribute($link['attributes']) . '>';
$item .= ($link['html'] ? $link['title'] : check_plain($link['title']));
$item .= '</span>';
}
$output .= '<li' . drupal_attributes(array('class' => $class)) . '>';
$output .= '<li' . new Attribute(array('class' => $class)) . '>';
$output .= $item;
$output .= '</li>';
}
......@@ -1722,7 +1723,7 @@ function theme_image($variables) {
}
}
return '<img' . drupal_attributes($attributes) . ' />';
return '<img' . new Attribute($attributes) . ' />';
}
/**
......@@ -1838,7 +1839,7 @@ function theme_table($variables) {
$attributes['class'][] = 'sticky-enabled';
}
$output = '<table' . drupal_attributes($attributes) . ">\n";
$output = '<table' . new Attribute($attributes) . ">\n";
if (isset($caption)) {
$output .= '<caption>' . $caption . "</caption>\n";
......@@ -1866,15 +1867,15 @@ function theme_table($variables) {
// Build colgroup
if (is_array($cols) && count($cols)) {
$output .= ' <colgroup' . drupal_attributes($attributes) . '>';
$output .= ' <colgroup' . new Attribute($attributes) . '>';
$i = 0;
foreach ($cols as $col) {
$output .= ' <col' . drupal_attributes($col) . ' />';
$output .= ' <col' . new Attribute($col) . ' />';
}
$output .= " </colgroup>\n";
}
else {
$output .= ' <colgroup' . drupal_attributes($attributes) . " />\n";
$output .= ' <colgroup' . new Attribute($attributes) . " />\n";
}
}
}
......@@ -1940,7 +1941,7 @@ function theme_table($variables) {
}
// Build row
$output .= ' <tr' . drupal_attributes($attributes) . '>';
$output .= ' <tr' . new Attribute($attributes) . '>';
$i = 0;
foreach ($cells as $cell) {
$cell = tablesort_cell($cell, $header, $ts, $i++);
......@@ -2018,7 +2019,7 @@ function theme_item_list($variables) {
$output = '';
if ($items) {
$output .= '<' . $type . drupal_attributes($list_attributes) . '>';
$output .= '<' . $type . new Attribute($list_attributes) . '>';
$num_items = count($items);
$i = 0;
......@@ -2064,7 +2065,7 @@ function theme_item_list($variables) {
$attributes['class'][] = 'last';
}
$output .= '<li' . drupal_attributes($attributes) . '>' . $value . '</li>';
$output .= '<li' . new Attribute($attributes) . '>' . $value . '</li>';
}
$output .= "</$type>";
}
......@@ -2129,7 +2130,7 @@ function theme_feed_icon($variables) {
*/
function theme_html_tag($variables) {
$element = $variables['element'];
$attributes = isset($element['#attributes']) ? drupal_attributes($element['#attributes']) : '';
$attributes = isset($element['#attributes']) ? new Attribute($element['#attributes']) : '';
if (!isset($element['#value'])) {
return '<' . $element['#tag'] . $attributes . " />\n";
}
......@@ -2211,7 +2212,7 @@ function theme_meter($variables) {
}
}
$output = '<div' . drupal_attributes($attributes) . '>';
$output = '<div' . new Attribute($attributes) . '>';
$output .= ' <div style="width: '. $variables['percentage'] .'%;" class="foreground"></div>';
$output .= "</div>\n";
......@@ -2266,7 +2267,7 @@ function _theme_table_cell($cell, $header = FALSE) {
$header |= isset($cell['header']);
unset($cell['data']);
unset($cell['header']);
$attributes = drupal_attributes($cell);
$attributes = new Attribute($cell);
}
else {
$data = $cell;
......@@ -2322,7 +2323,7 @@ function template_preprocess(&$variables, $hook) {
$default_variables = _template_preprocess_default_variables();
}
if (!isset($default_attributes)) {
$default_attributes = drupal_attributes(array('class' => array()));
$default_attributes = new Attribute(array('class' => array()));
}
$variables += $default_variables + array(
'attributes' => clone $default_attributes,
......@@ -2422,7 +2423,7 @@ function template_preprocess_html(&$variables) {
}
// Initializes attributes which are specific to the html and body elements.
$variables['html_attributes'] = drupal_attributes();
$variables['html_attributes'] = new Attribute;
// HTML element attributes.
$variables['html_attributes']['lang'] = $language_interface->langcode;
......
......@@ -31,11 +31,6 @@
* echo '<cat class="cat ' . $attributes['class'] . '" ' . $attributes . '>';
* // Produces <cat class="cat black-cat white-cat black-white-cat" id="socks">
* @endcode
*
* Most of the time this object is not created directly, but
* instantiated by drupal_attributes().
*
* @see drupal_attributes()
*/
class Attribute implements ArrayAccess, IteratorAggregate {
......
......@@ -6,6 +6,7 @@
*/
use Drupal\node\Node;
use Drupal\Core\Template\Attribute;
/**
* Implements hook_help().
......@@ -1245,7 +1246,7 @@ function template_preprocess_book_export_html(&$variables) {
*/
function template_process_book_export_html(&$variables) {
// Flatten out html_attributes
$variables['html_attributes'] = drupal_attributes($variables['html_attributes']);
$variables['html_attributes'] = new Attribute($variables['html_attributes']);
}
/**
......
......@@ -5,6 +5,7 @@
*/
use Drupal\entity\EntityFieldQuery;
use Drupal\Core\Template\Attribute;
/*
* Load all public Field API functions. Drupal currently has no
......@@ -1107,17 +1108,17 @@ function template_process_field(&$variables, $hook) {
static $default_attributes;
// The default theme implementation is a function, so template_process() does
// not automatically run, so we need to flatten the classes and attributes
// here. For best performance, only call drupal_attributes() when needed, and
// note that template_preprocess_field() does not initialize the
// *_attributes variables.
// here. For best performance, only instantiate Drupal\Core\Template\Attribute
// when needed, and note that template_preprocess_field() does not initialize
// the *_attributes variables.
if (!isset($default_attributes)) {
$default_attributes = drupal_attributes();
$default_attributes = new Attribute;
}
$variables['attributes'] = isset($variables['attributes']) ? drupal_attributes($variables['attributes']) : clone $default_attributes;
$variables['title_attributes'] = isset($variables['title_attributes']) ? drupal_attributes($variables['title_attributes']) : clone($default_attributes);
$variables['content_attributes'] = isset($variables['content_attributes']) ? drupal_attributes($variables['content_attributes']) : clone($default_attributes);
$variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes;
$variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone($default_attributes);
$variables['content_attributes'] = isset($variables['content_attributes']) ? new