diff --git a/includes/common.inc b/includes/common.inc
index 0c6c9eb..6d6615c 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2269,6 +2269,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
@@ -2357,7 +2384,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));
 }
 
 /**
@@ -4883,10 +4911,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/includes/form.inc b/includes/form.inc
index decca00..04a6850 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -2552,7 +2552,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));
 }
 
 /**
@@ -2665,7 +2665,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>';
@@ -2707,7 +2707,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);
 }
 
 /**
@@ -2731,7 +2731,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'] : '');
 }
 
 /**
@@ -2969,7 +2969,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);
 }
 
 /**
@@ -2992,7 +2992,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'] : '');
 }
 
 /**
@@ -3142,7 +3142,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']);
 }
 
 /**
@@ -3608,7 +3608,7 @@ function theme_button($variables) {
     $element['#attributes']['class'][] = 'form-button-disabled';
   }
 
-  return '<input' . drupal_attributes($element['#attributes']) . ' />';
+  return drupal_element('input', $element['#attributes'], TRUE);
 }
 
 /**
@@ -3637,7 +3637,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);
 }
 
 /**
@@ -3654,7 +3654,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";
 }
 
 /**
@@ -3685,10 +3685,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;
 }
@@ -3713,7 +3713,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>');
 }
 
 /**
@@ -3742,8 +3742,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;
 }
@@ -3765,7 +3765,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);
 }
 
 /**
@@ -3802,7 +3802,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);
 }
 
 /**
@@ -3878,7 +3878,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'])) {
@@ -3931,7 +3931,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, '*');
 }
 
 /**
@@ -3986,7 +3986,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/includes/menu.inc b/includes/menu.inc
index 0e3f5b6..3c39455 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -1551,7 +1551,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/includes/theme.inc b/includes/theme.inc
index f592891..066513e 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1371,7 +1371,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']));
 }
 
 /**
@@ -1430,14 +1431,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;
@@ -1456,7 +1453,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.
@@ -1467,11 +1464,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++;
@@ -1518,7 +1514,7 @@ function theme_image($variables) {
     }
   }
 
-  return '<img' . drupal_attributes($attributes) . ' />';
+  return drupal_element('img', $attributes, TRUE);
 }
 
 /**
@@ -1633,7 +1629,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";
@@ -1661,15 +1657,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";
       }
     }
   }
@@ -1735,7 +1731,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++);
@@ -1815,7 +1811,7 @@ function theme_item_list($variables) {
   }
 
   if (!empty($items)) {
-    $output .= "<$type" . drupal_attributes($attributes) . '>';
+    $output .= drupal_element($type, $attributes);
     $num_items = count($items);
     foreach ($items as $i => $item) {
       $attributes = array();
@@ -1847,7 +1843,7 @@ function theme_item_list($variables) {
       if ($i == $num_items - 1) {
         $attributes['class'][] = 'last';
       }
-      $output .= '<li' . drupal_attributes($attributes) . '>' . $data . "</li>\n";
+      $output .= drupal_element('li', $attributes, $data) . "\n";
     }
     $output .= "</$type>";
   }
@@ -1902,22 +1898,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";
 }
 
 /**
@@ -1961,7 +1958,7 @@ function theme_username($variables) {
     // Modules may have added important attributes so they must be included
     // in the output. Additional classes may be added as array elements like
     // $variables['attributes_array']['class'][] = 'myclass';
-    $output = '<span' . drupal_attributes($variables['attributes_array']) . '>' . $variables['name'] . $variables['extra'] . '</span>';
+    $output = drupal_element('span', $variables['attributes_array'], $variables['name'] . $variables['extra']);
   }
   return $output;
 }
@@ -2017,7 +2014,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'] : '';
@@ -2028,18 +2025,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/modules/book/book-navigation.tpl.php b/modules/book/book-navigation.tpl.php
index 5d8e9aa..d13dab7 100644
--- a/modules/book/book-navigation.tpl.php
+++ b/modules/book/book-navigation.tpl.php
@@ -36,13 +36,13 @@
     <?php if ($has_links): ?>
     <div class="page-links clearfix">
       <?php if ($prev_url): ?>
-        <a href="<?php print $prev_url; ?>" class="page-previous" title="<?php print t('Go to previous page'); ?>"><?php print t('‹ ') . $prev_title; ?></a>
+        <?php echo drupal_element('a', array('class' => 'page-previous', 'title' => t('Go to previous page'), 'href' => $prev_url), t('‹ ') . $prev_title); ?>
       <?php endif; ?>
       <?php if ($parent_url): ?>
-        <a href="<?php print $parent_url; ?>" class="page-up" title="<?php print t('Go to parent page'); ?>"><?php print t('up'); ?></a>
+        <?php echo drupal_element('a', array('class' => 'page-up', 'title' => t('Go to parent page'), 'href' => $parent_url), t('up')); ?>
       <?php endif; ?>
       <?php if ($next_url): ?>
-        <a href="<?php print $next_url; ?>" class="page-next" title="<?php print t('Go to next page'); ?>"><?php print $next_title . t(' ›'); ?></a>
+        <?php echo drupal_element('a', array('class' => 'page-next', 'title' => t('Go to next page'), 'href' => $next_url), $next_title . t(' ›')); ?>
       <?php endif; ?>
     </div>
     <?php endif; ?>
diff --git a/modules/filter/filter.module b/modules/filter/filter.module
index a3f787e..e43dc64 100644
--- a/modules/filter/filter.module
+++ b/modules/filter/filter.module
@@ -1137,7 +1137,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/modules/node/node.module b/modules/node/node.module
index 30e9559..807ed43 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -2507,7 +2507,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/modules/rdf/rdf.module b/modules/rdf/rdf.module
index ebecd42..6e4f410 100644
--- a/modules/rdf/rdf.module
+++ b/modules/rdf/rdf.module
@@ -828,8 +828,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;
 }
@@ -864,7 +864,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;
 }
