Generating a unique id

In a number or different contexts we want to generate HTML elements that have an id attribute. The result will only be valid XHTML if the id is unique on the page.

This is the development page for a new method that can generate id values while making sure they are actually unique. Of course the latter will only work reliably if every bit of Wikka code that (now) generates an id (or would need to) makes use of this method so it can keep track of already-used id values.

New makeId() method

The method will become part of the Wikka core, and have public access so user-contributed extensions can (should) make use of it.

The following code should be inserted in the //MISC section of wikka.php, right after the ReturnSafeHTML() method:
     * Create a unique id for an HTML element.
     * Although - given Wikka accepts can use embedded HTML - it cannot be
     * guaranteed that an id generated by this method is unique it tries its
     * best to make it unique:
     * - ids are organized into groups, with the group name used as a prefix
     * - if an id is specified it is compared with other ids in the same group;
     *   if an identical id exists within the same group, a sequence suffix is
     *   added, otherwise the specified id is accepted and recorded as a member
     *   of the group
     * - if no id is specified (or an invalid one) an id will be generated, and
     *   given a sequence suffix if needed
     * For headings, it is possible to derive an id from the heading content;
     * to support this, any embedded whitespace is replaced with underscores
     * to generate a recognizable id that will remain (mostly) constant even if
     * new headings are inserted in a page. (This is not done for embedded
     * HTML.)
     * The method supports embedded HTML as well: as long as the formatter
     * passes each id found in embedded HTML through this method it can take
     * care that the id is valid and unique.
     * This works as follows:
     * - indicate an 'embedded' id with group 'embed'
     * - NO prefix will be added for this reserved group
     * - ids will be recorded and checked for uniqueness and validity
     * - invalid ids are replaced
     * - already-existing ids in the group are given a sequence suffix
     * The result is that as long as the already-defined id is valid and
     * unique, it will be remain unchanged (but recorded to ensure uniqueness
     * overall).
     * @author      {@link JavaWoman}
     * @copyright   Copyright © 2005, Marjolein Katsma
     * @license GNU Lesser General Public License
     * @access  public
     * @uses    ID_LENGTH
     * @param   string  $group  required: id group (e.g. form, head); will be
     *                          used as prefix (except for the reserved group
     *                          'embed' to be used for embedded HTML only)
     * @param   string  $id     optional: id to use; if not specified or
     *                          invalid, an id will be generated; if not
     *                          unique, a sequence number will be appended
     * @return  string  resulting id

    function makeId($group,$id='')
        // initializations
        static $aSeq = array();                                     # group sequences
        static $aIds = array();                                     # used ids

        // preparation for group
        if (!preg_match('/^[A-Z-a-z]/',$group))                     # make sure group starts with a letter
            $group = 'g'.$group;
        if (!isset($aSeq[$group]))
            $aSeq[$group] = 0;
        if (!isset($aIds[$group]))
            $aIds[$group] = array();
        if ('embed' != $group)
            $id = preg_replace('/\s+/','_',trim($id));              # replace any whitespace sequence in $id with a single underscore

        // validation (full for 'embed', characters only for other groups since we'll add a prefix)
        if ('embed' == $group)
            $validId = preg_match('/^[A-Za-z][A-Za-z0-9_:.-]*$/',$id);  # ref:
            $validId = preg_match('/^[A-Za-z0-9_:.-]*$/',$id);

        // build or generate id
        if ('' == $id || !$validId || in_array($id,$aIds))          # ignore specified id if it is invalid or exists already
            $id = substr(md5($group.$id),0,ID_LENGTH);              # use group and id as basis for generated id
        $idOut = ('embed' == $group) ? $id : $group.'_'.$id;        # add group prefix (unless embedded HTML)
        if (in_array($id,$aIds[$group]))
            $idOut .= '_'.++$aSeq[$group];                          # add suffiX to make ID unique

        // result
        $aIds[$group][] = $id;                                      # keep track of both specified and generated ids (without suffix)
        return $idOut;


In order to ensure really unique ids on a page, it's important to actually use this method wherever an id is needed (or, in the case of embedded HTML, already used). Even existing ids will then be automatically validated and "recorded" to prevent duplicates from happening.

The method can take two parameters, the first of which, $group, is required. The optional parameter $id can be used to record and validate an existing or proposed id. A few (somewhat simplified) examples:

Supporting code

As can be seen (and is documented in the docblock) the new makeId() method uses a constant that doesn't exist yet in wikka.php.

ID_LENGTH constant

To avoid excessively long id strings when an id is generated, we take a substring of a shorter length; this length is set via ID_LENGTH. Alternatively, it could be made a configurable value via the configuration file - but for now I've just chosen a reasonable length.

See ArrayToList for the code (for this and some other constants) and where to add it.

There are 5 comments on this page. [Show comments]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki