diff --git a/core/includes/common.inc b/core/includes/common.inc
index 919bfb5..37ecd91 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1619,23 +1619,27 @@ function format_rss_item($title, $link, $description, $args = array()) {
 function format_xml_elements($array) {
   $output = '';
   foreach ($array as $key => $value) {
+    $name = FALSE;
     if (is_numeric($key)) {
       if ($value['key']) {
-        $output .= ' <' . $value['key'];
-        if (isset($value['attributes']) && is_array($value['attributes'])) {
-          $output .= drupal_attributes($value['attributes']);
-        }
-
+        $name = $value['key'];
+        $attributes = isset($value['attributes']) && is_array($value['attributes']) ? $value['attributes'] : array();
         if (isset($value['value']) && $value['value'] != '') {
-          $output .= '>' . (is_array($value['value']) ? format_xml_elements($value['value']) : check_plain($value['value'])) . '</' . $value['key'] . ">\n";
+          $content = $value['value'];
         }
         else {
-          $output .= " />\n";
+          $content = TRUE;
         }
       }
     }
     else {
-      $output .= ' <' . $key . '>' . (is_array($value) ? format_xml_elements($value) : check_plain($value)) . "</$key>\n";
+      $name = $key;
+      $attributes = array();
+      $content = $value;
+    }
+    if ($name !== FALSE) {
+      $content = is_array($content) ? format_xml_elements($content) : check_plain($content);
+      $output .= drupal_element($name, $attributes, $content) . "\n";
     }
   }
   return $output;
@@ -2235,6 +2239,33 @@ function drupal_attributes(array $attributes = array()) {
 }
 
 /**
+ * Generates either an HTML element open tag or a complete element.
+ *
+ * @param $name
+ *   The string tag name.
+ * @param $attributes
+ *   An associative array of key-value pairs to be converted to attributes.
+ * @param $content
+ *   If boolean FALSE (default), generate only the open tag.
+ *   If boolean TRUE, generate an XML-shorthand empty element.
+ *   If a string, use that as the contents of the element and generate the close tag.
+ *
+ * @return
+ *   The string of the complete open tag or complete element with open tag, content, and close tag.
+ */
+function drupal_element($name, array $attributes = array(), $content = FALSE) {
+  $output = '<' . $name . drupal_attributes($attributes);
+  if ($content === TRUE) {
+    $output .= ' /';
+  }
+  elseif (is_string($content)) {
+    $output .= '>' . $content . '</' . $name;
+  }
+  $output .= '>';
+  return $output;
+}
+
+/**
  * Formats an internal or external URL link as an HTML anchor tag.
  *
  * This function correctly handles aliased paths, and adds an 'active' class
@@ -2323,7 +2354,8 @@ 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>';
+  $options['attributes']['href'] = url($path, $options);
+  return drupal_element('a', $options['attributes'], $options['html'] ? $text : check_plain($text));
 }
 
 /**
@@ -4981,10 +5013,10 @@ function drupal_get_token($value = '') {
  * @param $value
  *   An additional value to base the token on.
  * @param $skip_anonymous
- *   Set to true to skip token validation for anonymous users.
+ *   Set to TRUE to skip token validation for anonymous users.
  * @return
  *   True for a valid token, false for an invalid token. When $skip_anonymous
- *   is true, the return value will always be true for anonymous users.
+ *   is TRUE, the return value will always be TRUE for anonymous users.
  */
 function drupal_valid_token($token, $value = '', $skip_anonymous = FALSE) {
   global $user;
diff --git a/core/includes/form.inc b/core/includes/form.inc
index a12fb34..36549ba 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -2572,7 +2572,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 drupal_element('select', $element['#attributes'], form_select_options($element));
 }
 
 /**
@@ -2685,7 +2685,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 = drupal_element('fieldset', $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>';
@@ -2727,7 +2727,7 @@ function theme_radio($variables) {
   }
   _form_set_class($element, array('form-radio'));
 
-  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+  return drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -2751,7 +2751,7 @@ function theme_radios($variables) {
   if (!empty($element['#attributes']['class'])) {
     $attributes['class'] .= ' ' . implode(' ', $element['#attributes']['class']);
   }
-  return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
+  return drupal_element('div', $attributes, !empty($element['#children']) ? $element['#children'] : '');
 }
 
 /**
@@ -2989,7 +2989,7 @@ function theme_checkbox($variables) {
   }
   _form_set_class($element, array('form-checkbox'));
 
-  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+  return drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3012,7 +3012,7 @@ function theme_checkboxes($variables) {
   if (!empty($element['#attributes']['class'])) {
     $attributes['class'] = array_merge($attributes['class'], $element['#attributes']['class']);
   }
-  return '<div' . drupal_attributes($attributes) . '>' . (!empty($element['#children']) ? $element['#children'] : '') . '</div>';
+  return drupal_element('div', $attributes, !empty($element['#children']) ? $element['#children'] : '');
 }
 
 /**
@@ -3162,7 +3162,7 @@ function theme_container($variables) {
     $element['#attributes']['class'][] = 'form-wrapper';
   }
 
-  return '<div' . drupal_attributes($element['#attributes']) . '>' . $element['#children'] . '</div>';
+  return drupal_element('div', $element['#attributes'], $element['#children']);
 }
 
 /**
@@ -3628,7 +3628,7 @@ function theme_button($variables) {
     $element['#attributes']['class'][] = 'form-button-disabled';
   }
 
-  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+  return drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3657,7 +3657,7 @@ function theme_image_button($variables) {
     $element['#attributes']['class'][] = 'form-button-disabled';
   }
 
-  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+  return drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3674,7 +3674,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 drupal_element('input', $element['#attributes'], TRUE) . "\n";
 }
 
 /**
@@ -3705,10 +3705,10 @@ function theme_textfield($variables) {
     $attributes['value'] = url($element['#autocomplete_path'], array('absolute' => TRUE));
     $attributes['disabled'] = 'disabled';
     $attributes['class'][] = 'autocomplete';
-    $extra = '<input' . drupal_attributes($attributes) . ' />';
+    $extra = drupal_element('input', $attributes, TRUE);
   }
 
-  $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
+  $output = drupal_element('input', $element['#attributes'], TRUE);
 
   return $output . $extra;
 }
@@ -3733,7 +3733,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 drupal_element('form', $element['#attributes'], '<div>' . $element['#children'] . '</div>');
 }
 
 /**
@@ -3762,8 +3762,8 @@ function theme_textarea($variables) {
     $wrapper_attributes['class'][] = 'resizable';
   }
 
-  $output = '<div' . drupal_attributes($wrapper_attributes) . '>';
-  $output .= '<textarea' . drupal_attributes($element['#attributes']) . '>' . check_plain($element['#value']) . '</textarea>';
+  $output = drupal_element('div', $wrapper_attributes);
+  $output .= drupal_element('textarea', $element['#attributes'], check_plain($element['#value']));
   $output .= '</div>';
   return $output;
 }
@@ -3785,7 +3785,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 drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3822,7 +3822,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 drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3898,7 +3898,7 @@ function theme_form_element($variables) {
   if (!empty($element['#attributes']['disabled'])) {
     $attributes['class'][] = 'form-disabled';
   }
-  $output = '<div' . drupal_attributes($attributes) . '>' . "\n";
+  $output = drupal_element('div', $attributes) . "\n";
 
   // If #title is not set, we don't display any label or required marker.
   if (!isset($element['#title'])) {
@@ -3951,7 +3951,7 @@ function theme_form_required_marker($variables) {
     'class' => 'form-required',
     'title' => $t('This field is required.'),
   );
-  return '<abbr' . drupal_attributes($attributes) . '>*</abbr>';
+  return drupal_element('abbr', $attributes, '*');
 }
 
 /**
@@ -4006,7 +4006,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 ' ' . drupal_element('label', $attributes, $t('!title !required', array('!title' => $title, '!required' => $required))) . "\n";
 }
 
 /**
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index f23eb0d..b14db80 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -1605,7 +1605,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 drupal_element('li', $element['#attributes'], $output . $sub_menu) . "\n";
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 68dd70b..d89718d 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1519,7 +1519,8 @@ 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>';
+  $variables['options']['attributes']['href'] = url($variables['path'], $variables['options']);
+  return drupal_element('a', $variables['options']['attributes'], $variables['options']['html'] ? $variables['text'] : check_plain($variables['text']));
 }
 
 /**
@@ -1579,14 +1580,10 @@ function theme_links($variables) {
           'level' => 'h2',
         );
       }
-      $output .= '<' . $heading['level'];
-      if (!empty($heading['class'])) {
-        $output .= drupal_attributes(array('class' => $heading['class']));
-      }
-      $output .= '>' . check_plain($heading['text']) . '</' . $heading['level'] . '>';
+      $output .= drupal_element($heading['level'], empty($heading['class']) ? array() : array('class' => $heading['class']), check_plain($heading['text']));
     }
 
-    $output .= '<ul' . drupal_attributes($attributes) . '>';
+    $output .= drupal_element('ul', $attributes);
 
     $num_links = count($links);
     $i = 1;
@@ -1605,7 +1602,7 @@ function theme_links($variables) {
           && (empty($link['language']) || $link['language']->language == $language_url->language)) {
         $class[] = 'active';
       }
-      $output .= '<li' . drupal_attributes(array('class' => $class)) . '>';
+      $output .= drupal_element('li', array('class' => $class));
 
       if (isset($link['href'])) {
         // Pass in $link as $options, they share the same keys.
@@ -1616,11 +1613,10 @@ function theme_links($variables) {
         if (empty($link['html'])) {
           $link['title'] = check_plain($link['title']);
         }
-        $span_attributes = '';
-        if (isset($link['attributes'])) {
-          $span_attributes = drupal_attributes($link['attributes']);
+        if (!isset($link['attributes'])) {
+          $link['attributes'] = array();
         }
-        $output .= '<span' . $span_attributes . '>' . $link['title'] . '</span>';
+        $output .= drupal_element('span', $link['attributes'], $link['title']);
       }
 
       $i++;
@@ -1667,7 +1663,7 @@ function theme_image($variables) {
     }
   }
 
-  return '<img' . drupal_attributes($attributes) . ' />';
+  return drupal_element('img', $attributes, TRUE);
 }
 
 /**
@@ -1782,7 +1778,7 @@ function theme_table($variables) {
     $attributes['class'][] = 'sticky-enabled';
   }
 
-  $output = '<table' . drupal_attributes($attributes) . ">\n";
+  $output = drupal_element('table', $attributes) . "\n";
 
   if (isset($caption)) {
     $output .= '<caption>' . $caption . "</caption>\n";
@@ -1810,15 +1806,15 @@ function theme_table($variables) {
 
       // Build colgroup
       if (is_array($cols) && count($cols)) {
-        $output .= ' <colgroup' . drupal_attributes($attributes) . '>';
+        $output .= ' ' . drupal_element('colgroup', $attributes);
         $i = 0;
         foreach ($cols as $col) {
-          $output .= ' <col' . drupal_attributes($col) . ' />';
+          $output .= ' ' . drupal_element('col', $col, TRUE);
         }
         $output .= " </colgroup>\n";
       }
       else {
-        $output .= ' <colgroup' . drupal_attributes($attributes) . " />\n";
+        $output .= ' ' . drupal_element('colgroup', $attributes, TRUE) . "\n";
       }
     }
   }
@@ -1884,7 +1880,7 @@ function theme_table($variables) {
         }
 
         // Build row
-        $output .= ' <tr' . drupal_attributes($attributes) . '>';
+        $output .= ' ' . drupal_element('tr', $attributes);
         $i = 0;
         foreach ($cells as $cell) {
           $cell = tablesort_cell($cell, $header, $ts, $i++);
@@ -1962,7 +1958,7 @@ function theme_item_list($variables) {
 
   $output = '';
   if ($items) {
-    $output .= '<' . $type . drupal_attributes($list_attributes) . '>';
+    $output .= drupal_element($type, $list_attributes);
 
     $num_items = count($items);
     $i = 0;
@@ -2008,7 +2004,7 @@ function theme_item_list($variables) {
         $attributes['class'][] = 'last';
       }
 
-      $output .= '<li' . drupal_attributes($attributes) . '>' . $value . '</li>';
+      $output .= drupal_element('li', $attributes, $value);
     }
     $output .= "</$type>";
   }
@@ -2071,22 +2067,23 @@ 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']) ? $element['#attributes'] : array();
+
   if (!isset($element['#value'])) {
-    return '<' . $element['#tag'] . $attributes . " />\n";
+    $content = TRUE;
   }
   else {
-    $output = '<' . $element['#tag'] . $attributes . '>';
+    $content = '';
     if (isset($element['#value_prefix'])) {
-      $output .= $element['#value_prefix'];
+      $content .= $element['#value_prefix'];
     }
-    $output .= $element['#value'];
+    $content .= $element['#value'];
     if (isset($element['#value_suffix'])) {
-      $output .= $element['#value_suffix'];
+      $content .= $element['#value_suffix'];
     }
-    $output .= '</' . $element['#tag'] . ">\n";
-    return $output;
   }
+
+  return drupal_element($element['#tag'], $attributes, $content) . "\n";
 }
 
 /**
@@ -2152,7 +2149,7 @@ function theme_indentation($variables) {
  *   HTML for the cell.
  */
 function _theme_table_cell($cell, $header = FALSE) {
-  $attributes = '';
+  $attributes = array();
 
   if (is_array($cell)) {
     $data = isset($cell['data']) ? $cell['data'] : '';
@@ -2163,18 +2160,19 @@ function _theme_table_cell($cell, $header = FALSE) {
     $header |= isset($cell['header']);
     unset($cell['data']);
     unset($cell['header']);
-    $attributes = drupal_attributes($cell);
+    $attributes = $cell;
   }
   else {
     $data = $cell;
   }
 
   if ($header) {
-    $output = "<th$attributes>$data</th>";
+    $name = 'th';
   }
   else {
-    $output = "<td$attributes>$data</td>";
+    $name = 'td';
   }
+  $output = drupal_element($name, $attributes, $data);
 
   return $output;
 }
diff --git a/core/modules/book/book-navigation.tpl.php b/core/modules/book/book-navigation.tpl.php
index 53f03c0..777e674 100644
--- a/core/modules/book/book-navigation.tpl.php
+++ b/core/modules/book/book-navigation.tpl.php
@@ -36,17 +36,17 @@
       <ul class="book-pager">
       <?php if ($prev_url): ?>
         <li class="previous">
-          <a href="<?php print $prev_url; ?>" rel="prev" title="<?php print t('Go to previous page'); ?>"><b><?php print t('‹'); ?></b> <?php print $prev_title; ?></a>
+          <a rel="prev" title="<?php print t('Go to previous page'); ?>" href="<?php print $prev_url; ?>"><b><?php print t('‹'); ?></b> <?php print $prev_title; ?></a>
         </li>
       <?php endif; ?>
       <?php if ($parent_url): ?>
         <li class="up">
-          <a href="<?php print $parent_url; ?>" title="<?php print t('Go to parent page'); ?>"><?php print t('up'); ?></a>
+          <a title="<?php print t('Go to parent page'); ?>" href="<?php print $parent_url; ?>"><?php print t('up'); ?></a>
         </li>
       <?php endif; ?>
       <?php if ($next_url): ?>
         <li class="next">
-          <a href="<?php print $next_url; ?>" rel="next" title="<?php print t('Go to next page'); ?>"><?php print $next_title;?> <b><?php print t('›'); ?></b></a>
+          <a rel="next" title="<?php print t('Go to next page'); ?>" href="<?php print $next_url; ?>"><?php print $next_title;?> <b><?php print t('›'); ?></b></a>
         </li>
       <?php endif; ?>
     </ul>
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index e857224..54439ba 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -1144,7 +1144,7 @@ function theme_filter_guidelines($variables) {
   $format = $variables['format'];
   $attributes['class'][] = 'filter-guidelines-item';
   $attributes['class'][] = 'filter-guidelines-' . $format->format;
-  $output = '<div' . drupal_attributes($attributes) . '>';
+  $output = drupal_element('div', $attributes);
   $output .= '<h3>' . check_plain($format->name) . '</h3>';
   $output .= theme('filter_tips', array('tips' => _filter_tips($format->format, FALSE)));
   $output .= '</div>';
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index c429fe5..8ca5a1f 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2510,7 +2510,10 @@ function node_feed($nids = FALSE, $channel = array()) {
   $channel = array_merge($channel_defaults, $channel);
 
   $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
-  $output .= "<rss version=\"" . $channel["version"] . "\" xml:base=\"" . $base_url . "\" " . drupal_attributes($namespaces) . ">\n";
+  $attributes = $namespaces;
+  $attributes['version'] = $channel["version"];
+  $attributes['xml:base'] = $base_url;
+  $output .= drupal_element('rss', $attributes) . "\n";
   $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language'], $channel_extras);
   $output .= "</rss>\n";
 
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 4eea417..7f861ac 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -843,8 +843,8 @@ function rdf_preprocess_image(&$variables) {
 function theme_rdf_template_variable_wrapper($variables) {
   $output = $variables['content'];
   if (!empty($output) && !empty($variables['attributes'])) {
-    $attributes = drupal_attributes($variables['attributes']);
-    $output = $variables['inline'] ? "<span$attributes>$output</span>" : "<div$attributes>$output</div>";
+    $name = $variables['inline'] ? 'span' : 'div';
+    $output = drupal_element($name, $variables['attributes'], $output);
   }
   return $output;
 }
@@ -879,7 +879,7 @@ function theme_rdf_metadata($variables) {
     // be used, but for maximum browser compatibility, W3C recommends the
     // former when serving pages using the text/html media type, see
     // http://www.w3.org/TR/xhtml1/#C_3.
-    $output .= '<span' . drupal_attributes($attributes) . '></span>';
+    $output .= drupal_element('span', $attributes, '');
   }
   return $output;
 }
