function drupal_html_id

Prepares a string for use as a valid HTML ID and guarantees uniqueness.

This function ensures that each passed HTML ID value only exists once on the page. By tracking the already returned ids, this function enables forms, blocks, and other content to be output multiple times on the same page, without breaking (X)HTML validation.

For already existing IDs, a counter is appended to the ID string. Therefore, JavaScript and CSS code should not rely on any value that was generated by this function and instead should rely on manually added CSS classes or similarly reliable constructs.

Two consecutive hyphens separate the counter from the original ID. To manage uniqueness across multiple Ajax requests on the same page, Ajax requests POST an array of all IDs currently present on the page, which are used to prime this function's cache upon first invocation.

To allow reverse-parsing of IDs submitted via Ajax, any multiple consecutive hyphens in the originally passed $id are replaced with a single hyphen.

Parameters

$id: The ID to clean.

Return value

The cleaned ID.

17 calls to drupal_html_id()
ArgumentPluginBase::processContainerRadios in drupal/core/modules/views/lib/Drupal/views/Plugin/views/argument/ArgumentPluginBase.php
Custom form radios process function.
CommentFormController::form in drupal/core/modules/comment/lib/Drupal/comment/CommentFormController.php
Overrides Drupal\Core\Entity\EntityFormController::form().
DisplayPluginBase::optionLink in drupal/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
Because forms may be split up into sections, this provides an easy URL to exactly the right section. Don't override this.
drupal_prepare_form in drupal/core/includes/form.inc
Prepares a structured form array.
drupal_pre_render_link in drupal/core/includes/common.inc
Pre-render callback: Renders a link into #markup.

... See full list

2 string references to 'drupal_html_id'
drupal_process_form in drupal/core/includes/form.inc
Processes a form submission.
HtmlIdentifierUnitTest::testDrupalHTMLId in drupal/core/modules/system/lib/Drupal/system/Tests/Common/HtmlIdentifierUnitTest.php
Tests that drupal_html_id() cleans the ID properly.

File

drupal/core/includes/common.inc, line 3421
Common functions that many Drupal modules will need to reference.

Code

function drupal_html_id($id) {

  // If this is an Ajax request, then content returned by this page request will
  // be merged with content already on the base page. The HTML IDs must be
  // unique for the fully merged content. Therefore, initialize $seen_ids to
  // take into account IDs that are already in use on the base page.
  $seen_ids_init =& drupal_static(__FUNCTION__ . ':init');
  if (!isset($seen_ids_init)) {

    // Ideally, Drupal would provide an API to persist state information about
    // prior page requests in the database, and we'd be able to add this
    // function's $seen_ids static variable to that state information in order
    // to have it properly initialized for this page request. However, no such
    // page state API exists, so instead, ajax.js adds all of the in-use HTML
    // IDs to the POST data of Ajax submissions. Direct use of $_POST is
    // normally not recommended as it could open up security risks, but because
    // the raw POST data is cast to a number before being returned by this
    // function, this usage is safe.
    if (empty($_POST['ajax_html_ids'])) {
      $seen_ids_init = array();
    }
    else {

      // This function ensures uniqueness by appending a counter to the base id
      // requested by the calling function after the first occurrence of that
      // requested id. $_POST['ajax_html_ids'] contains the ids as they were
      // returned by this function, potentially with the appended counter, so
      // we parse that to reconstruct the $seen_ids array.
      $ajax_html_ids = explode(' ', $_POST['ajax_html_ids']);
      foreach ($ajax_html_ids as $seen_id) {

        // We rely on '--' being used solely for separating a base id from the
        // counter, which this function ensures when returning an id.
        $parts = explode('--', $seen_id, 2);
        if (!empty($parts[1]) && is_numeric($parts[1])) {
          list($seen_id, $i) = $parts;
        }
        else {
          $i = 1;
        }
        if (!isset($seen_ids_init[$seen_id]) || $i > $seen_ids_init[$seen_id]) {
          $seen_ids_init[$seen_id] = $i;
        }
      }
    }
  }
  $seen_ids =& drupal_static(__FUNCTION__, $seen_ids_init);
  $id = strtr(drupal_strtolower($id), array(
    ' ' => '-',
    '_' => '-',
    '[' => '-',
    ']' => '',
  ));

  // As defined in http://www.w3.org/TR/html4/types.html#type-name, HTML IDs can
  // only contain letters, digits ([0-9]), hyphens ("-"), underscores ("_"),
  // colons (":"), and periods ("."). We strip out any character not in that
  // list. Note that the CSS spec doesn't allow colons or periods in identifiers
  // (http://www.w3.org/TR/CSS21/syndata.html#characters), so we strip those two
  // characters as well.
  $id = preg_replace('/[^A-Za-z0-9\\-_]/', '', $id);

  // Removing multiple consecutive hyphens.
  $id = preg_replace('/\\-+/', '-', $id);

  // Ensure IDs are unique by appending a counter after the first occurrence.
  // The counter needs to be appended with a delimiter that does not exist in
  // the base ID. Requiring a unique delimiter helps ensure that we really do
  // return unique IDs and also helps us re-create the $seen_ids array during
  // Ajax requests.
  if (isset($seen_ids[$id])) {
    $id = $id . '--' . ++$seen_ids[$id];
  }
  else {
    $seen_ids[$id] = 1;
  }
  return $id;
}